Introduction change

With this final assignment both an open water model and a groundwater model will be developed which will be coupled.
De open water model represents a small river called the Hooge Raam. The groundwater model will simulate the transition of precipitation into drainage and surface runoff of the area surrounding the Hooge Raam.
Flow in the open water model is based on the diffusive wave (“Froude = 0”) approximation in 1D. Groundwater flow is based on Darcy’s equation and simulates the flow between two open water courses (ditches or drains) in 1D. Groundwater flow will be simulated in transient mode to account for storage changes. The open water flow model simulates in pseudo stationary mode.
A storm event, a heavy rain shower, is the forcing for which we will investigate flow from the surface into the soil, towards ditches and finally to the Hooge Raam to get discharged at the weir on the lower side.

The Hooge Raam is located just below the small town Grave and is part of the water board “Aa en Maas”. The surrounding area is mainly used as grasslands, some cornfields and a few forest plots. The area is reasonably flat where surface levels range from 15.5 till about 17.5 m AMSL (Above Mean Sea Level).

The figure below gives an overview of the Hooge Raam which will be considered for modeling.

Overview Hooge Raam and area
Overview Hooge Raam and area

The first part contains the explanation and assignments for the open water model, the second will go into the details of the groundwater model, the coupling of both models is described in the third part and finally the last part (4) contains the setup to determine derived uncertainties of the “model-train”.

PART 1 the Open water model

Open water equations

This document should give an introduction to the hydraulic modeling of the Hooge Raam river.

An attempt is made to make the document self contained. For that reason we start by giving some basic formulas, and more formulas will follow further in the document. This document should however not to be considered as a course in hydrodynamics. The formulas are cited (which should be for most of you a repetition?) to form the base of the code.

The flow in rivers as the Hooge Raam is most often described by the so called St-Venant equations, expressing respectively the mass and the momentum balance (only in x-direction:

\[ \frac{\partial A}{\partial t} + \frac{\partial Q}{\partial x} = I\\ \frac{\partial Q}{\partial t} + \frac{\partial Q\,u}{\partial x} = g\;A\;\Big( S_o - S_f - \frac{\partial a}{\partial x}\Big) \]

As we are here less interested in highly dynamic open water calculations, a simplification of the second equation above will be used. This simplification starts from the observation that in many situations (certainly lowland situations) the first two terms in the momentum part of the St-Venant equations are much smaller than the terms on the right hand side.

\[ \frac{\partial Q}{\partial t} + \frac{\partial Q\,u}{\partial x} = g\;A\;\Big( S_o - S_f - \frac{\partial a}{\partial x}\Big)\\ \Downarrow \\ 0 = g\;A\;\Big( S_o - S_f - \frac{\partial a}{\partial x}\Big)\\ \Downarrow \\ 0 = S_o - S_f - \frac{\partial a}{\partial x} \]

The meaning of the terms in these equations will be explained when they first appear in this document.

In these exercises situations will be studied where

  • the flow in the Hooge Raam can be considered to be stationary (so the time derivatives in the equations above disappear)
  • there is a weir at the downstream end of the river

Situations of this type are often called backwater curves.

This type of open water model that will be developed in this document will be coupled with a groundwater model. So at some places typical groundwater terms and dimensions may occur.

The open water has its own standard units:

  • length unit = \(m\) meter
  • time unit = \(s\) second

Hooge Raam dimensions and parameters

We are going to simulate the open water flow of the Hooge Raam starting at the southern part at the “Vliesweg” with a prescribed flux coming from the discharging upstream area. At the northern part of the Hooge Raam section it discharges through a weir, which finally is getting discharged into the Meuse.

We will use the following data for dimensioning the river. Data is based on “legger” waterboard Aa en Maas.

You may want to quickly browse through this “legger” to see what information is available. Go to https://www.aaenmaas.nl/onswerk/regels/legger/ and open the ‘Legger oppervlaktewater’.

The length of this reach of the Hooge Raam is 1470 m. Upstream bottom level 14.50 m AMSL, downstream bottom level 11.80 m AMSL, (average) bottom width 2.10 m, (average) side slope 1.5 m/m.

Weir: crest width 2.0 m, crest height: 11.45 and 12.60 m.

Influx stationary upstream based on base flow as discharge of an upstream area of 250 ha (assume a discharge of 0.5 l/s/ha).

rm(list = ls())
HR.Qin = 250*0.005  #250ha * 0.5 l/s/ha
cat('Total influx upstream : ',HR.Qin)
Total influx upstream :  1.25

Longitudinal view

It is conventional to measure the length along the river from upstream to downstream. The domain of the river starts at 0 m.

We will also need the bottom slope in what follows. In the equations of the introduction this slope was called \(S_o\). It is defined as the drop of bottom level per unit length of the river. Here it is just a constant:

HR.length = 1470
HR.domain = c(0,HR.length)
HR.botelev.upstream = 14.50
HR.botelev.downstream = 11.80
HR.So = (HR.botelev.upstream -HR.botelev.downstream)/(HR.length) #% botelev.upstream #% botelev.dnstream #% length
cat('So = ',HR.So)
So =  0.001836735
HR.zb = approxfun(HR.domain,c(HR.botelev.upstream,HR.botelev.downstream),
                  rule=2)
plot(HR.domain,HR.zb(HR.domain),type = "l",col="brown",lwd=4,
     main="Hooge Raam bed bottom",xlab="x (m)", ylab= "z (m)")
grid()

This bottom slope brings the gravity into the equations: the higher this slope, the larger the influence of the gravity on the water flow.

Cross section geometry

The cross section can in general have a complex and spatially varying form. Her we take just a simple trapezoidal profile with a constant bottom width \(b\) and constant side slope \(m\):


HR.b=2.1   # bottom  width
HR.m=1.5   # side slope

The water level with respect to the bottom of this cross section is called the water depth and will be denoted by \(a\). To make a plot of the cross section we need to calculate the width for a whole range of possible water depths.

For a trapezoidal cross section this results in:


HR.width = function(a)
{
  return(HR.b+2*HR.m*a)
}

The next chunk creates a plot of the cross section


arange=seq(0,1.5,length=100) # large enough of possible depth values 
widthrange = HR.width(arange)
plot(c(-rev(widthrange/2),widthrange/2),c(rev(arange),arange),
     xlab="y",ylab="a",main="cross section",type="l",lwd=5,col="brown")
grid(col="black")

Wetted area

One of the important cross section functions is the wetted area. It was in the St-Venant equation denoted by \(A\). For a trapezoidal cross section this can be easily calculated by the following function:


HR.A = function(a)
{
  return(HR.b*a+HR.m*a^2)
}

One of the reasons that this wetted cross section is so important is because of the important formula \(Q = v A\), were \(Q\) is the discharge, \(v\) is the velocity and \(A\) is the wetted cross section. Some typical (max) velocities depending of the soil type are given the table below

source: Cultuurtechnisch Vademecum 1988
Soil type velocity m/s
clay/loam/loess 0.60 - 0.80
sand/solid peat 0.30 - 0.60
coarse sand 0.20 - 0.50
fine sand/soft peat 0.15 - 0.30

As can be seen in the function above that the wetted cross section is dependent on the water depth \(a\). One way to see the influence of the wetted cross section on the discharge is to investigate the relationship between \(Q\) and \(a\) for fixed velocities. Here is a typical example (it is customary to plot water depths on the vertical axis):

Arange =  HR.A(arange)
vel = 0.50 #m/s velocity for coarse sand
Qrange = vel*Arange
plot(Qrange,arange,
     main="Hooge Raam Q-a relation for fixed velocity v",
     xlab="Q (m^3/s)",ylab="a (m)",col="brown",lwd=3,type="l")
legend("bottomright", inset=.05, title="v (m/s)",
       c("0.5"), col=c("brown"),lwd=3,horiz=TRUE)
grid(col="black")

Velocities higher than 1 m/s are however rather atypical.

Fill the chunk below (replace all XXX) with a code that combines in one
plot the a-Q for four realistic velocities.

Arange =  HR.A(arange) #Calculating wetted area
Q1range = 0.15*Arange  #Clay/loam/loess velocity
Q2range=  0.20*Arange   #Sand/solid peat velocity
Q3range=  0.30*Arange  #Coarse sand 
Q4range = 0.60*Arange     #Fine sand/soft peat
plot(Q4range,arange,   #Make a plot of fine sand
     main="Hooge Raam Q-a relation for fixed velocity v", #Title
     xlab=expression(Q~(m^3/s)),ylab="a (m)",col="brown",lwd=3,type="l", ylim=c(0,1.5), xlim = c(0,2)) #Settings for plot
lines(Q3range,arange,lwd=3,col="blue") #Adding plot of coarse sand
lines(Q2range,arange,lwd=3,col="red")  #Adding plot of sand/solid peat
lines(Q1range,arange,lwd=3,col="green") #Adding plot of clay/loam/loess

legend("bottomright", inset=c(0.001), title="v (m/s)", #Adding the legend
c("clay, 0.15","sand, 0.20","coarse sand, 0.30","fine sand, 0.60"), col=c("green","red","blue","brown"),lwd=3,horiz=TRUE, cex = 0.6) #Color the difference scenario

grid(col="black") #Making the color of the grid to be black

NA
NA

Equilibrium discharge

But of course usually both and \(A\) and \(v\) change if the water depth \(a\) changes.

For equilibrium situations this relationship can be investigated through formulas. Equilibrium means that the water depth is constant in space (\(\frac{\partial a}{\partial x} = 0\)) in terms of the St-Venant equations of the introduction) and time. For this situation the open water equation simplifies into; \[S_0 = S_f = \frac{n^2\; Q^2}{A^2\; R^{4/3}}\]

From this one can derive: \[Q_\mathrm{equi} = \frac{\sqrt{S_o}}{n} A\; R^{2/3}\] where

  • \(S_o\): bottom slope (as calculated above)

  • \(n\): Manning coefficient: the higher this number, the more friction, typical values are around 0.04, but difficult to assess

  • \(A\): the wetted cross section, as discussed and calculated above

  • \(R\): hydraulic radius. Formally defined as wetted Area divided by the wetted perimeter(\(R=\frac {A}{P}\)), as in the next chunk:

HR.n = 0.045 

HR.R = function(a)
{
  return((HR.b*a+HR.m*a^2)/(HR.b+2*sqrt(1+HR.m^2)*a))
}
HR.Qequi = function(a)
{
  R = HR.R(a)
  A = HR.A(a)
  Q = sqrt(HR.So)/HR.n*A*R^(2/3) #% =
  return(sqrt(HR.So)/HR.n*A*R^(2/3))
}

Qequirange = HR.Qequi(arange)
plot(Qequirange,arange,
     main="Hooge Raam Q-a equilibrium",
     xlab="Qequi",ylab="a",lwd=3,col="blue",type="l")
grid(col="black")

Sometimes the question is posed the other way around. Assume that one wants to drain a constant intensity of 10 \(mm/day\) of an area of the size of 30 \(km^2\) through this river.

Step1: calculate the discharge through the river.

area = 30 # km^2
area = area *(1000)^2 # m^2
I = 10 # mm /day
I = I/(1000*24*3600) # m/s #+ = 
Q = I*area

Now we want to calculate the water level corresponding to the equilibrium discharge just calculated. We can use the plot made above, or we can use the following chunk to find the water level corresponding to the calculated discharge rate.

#install.packages("nleqslv")
library(nleqslv)
tosolve = function(a){return (HR.Qequi(a)-Q)}
aequi = nleqslv(1,tosolve)$x
print(aequi)
[1] 1.177077

Repeat the calculation above for the rainfall intensities between 0.5 mm/day and 15 mm/day and make a plot of the water depths vs these intensities.

Is = seq(0.5,15,length=100)
aequis = c()
for( i in 1:length(Is))
{
  I = Is[i]/(3600*24*1000)
  Q = I*area                
  tosolve = function(a){return (HR.Qequi(a)-Q)}
  aequis[i] = nleqslv(1,tosolve)$x
}
plot(Is,aequis,type="l",col="blue",lwd=3, main="Water Depth for Different Rain Intensities", xlab = "Rainfall intensity (mm/day)", ylab = "Steady state water level (m)")
grid(col="black")

The downstream weir

Geometry of the weir

See the following graph

HR.weir.width = 2.25  #m width of the weir floor 
HR.weir.crest = 1 #m above bottom river
# draw weir in cross section
plot(c(-rev(widthrange/2),widthrange/2),c(rev(arange),arange),
     xlab="y (m)",ylab="a (m)",main="cross section",type="l",lwd=5,col="brown")
lines(c(-HR.weir.width/2,-HR.weir.width/2,HR.weir.width/2,HR.weir.width/2),
      c(max(arange),HR.weir.crest,HR.weir.crest,max(arange))
      ,col="blue",lwd=3)
legend("bottomright", inset=.05,
       c("river","weir"), col=c("brown","blue"),lwd=3,horiz=FALSE)
grid(col="black")

Weir Q-a relation

The amount of water flowing over a weir is a function of the water height above the crest. For a rectangular weir this is a well known formula: \[Q_\mathrm{weir} = 1.83\; B_\mathrm{weir}\; (a-c_\mathrm{weir})^{3/2}\]

Finish the code below such that the function calculates discharges according to the formula above.

HR.weirQ = function(a)
{
  a[a<HR.weir.crest] = HR.weir.crest
  return(1.83*HR.weir.width*(a-HR.weir.crest)^(3/2))
}

weirQrange = HR.weirQ(arange)

## MAKE THE PLOT NICER BY ADJUSTING XLIM YLIM AND LEGEND

plot(Qequirange,arange,main="Qequi (without weir) and Qweir for HoogeRaam",
     xlab="Qequi",ylab="a",lwd=3,col="blue",type="l", ylim = c(0,1.5))
lines(weirQrange,arange,lwd=3,col="red")
legend("bottomright", inset=.05,
       c("Qequiriver","Qweir"), col=c("blue","red"),lwd=3,horiz=FALSE)
grid(col="black")

Rough backwater approximation

For any given fixed discharge \(Q\) that goes through the Hooge Raam, we can calculate two water levels with the information above:

In between the water levels will vary between these two. Here we first are going to calculate these levels and then just as a first rough approximation interpolate linear in between.

Calculate for Q=1.2 the water depth at the weir and the water depth at the upstream end

library(nleqslv)
Q = 1.2
#Water level far upstream
tosolve = function(a){return (HR.Qequi(a)-Q)}
aequi = nleqslv(1,tosolve)$x
print(aequi)
[1] 0.675549
#Water level at the weir
tosolve = function(a){return (HR.weirQ(a)-Q)}
aweir = nleqslv(1.1,tosolve)$x
print(aweir)
[1] 1.439574

Make a function for the rough linear approximation and plot it

HR.bwrough = approxfun(HR.domain,
                       c(HR.botelev.upstream+aequi,HR.botelev.downstream+aweir),rule=2) #% aequi #% aweir
plot(HR.domain,HR.bwrough(HR.domain),ylim=c(HR.botelev.downstream,HR.botelev.upstream+aequi),
     main=paste("rough level approx for Q=",Q),
     xlab="l (m)",ylab="h (m)",col="blue",lwd=4,type="l")

lines(HR.domain,HR.zb(HR.domain),col="brown",lwd=4)


#Add legend, blue is water elevation and brown is river bed elevation

Setup of base backwater model

The previous backwater approximation is too rough. In reality the water levels will vary non-linear (but still very smoothly). A 1D flow model has to be setup to calculate these levels. So the 1D flow library needs to be loaded:

library(FVFE1D)

The choice of state

For all kind of local calculations the water depth \(a\) is the most common choice. For non-local problems as the calculation of a backwater curve, the water level with respect to AMSL (the \(h\) in the plot above) is the most common choice. These two quantities are connected through the river, bottom height:
\[ h = z_b(x)+a \]

For the Hooge Raam, a function for the bottom was already given: HR.zb (see above).

The equation

Finish the function definition below:

HR.a = function(x,h)
{
  return(h-HR.zb(x)) #
}

The internal flux function

The theory

The most important step in defining a flow model is the definition of the internal flux. The definition follows from the equation as given at the start of this document:

\[ 0 = S_o - S_f - \frac{\partial a}{\partial x} \]

The formula for the friction slope, \(S_f\), requires now a bit more sophistication.

The flow can now be to the right (\(Q>0\)) or to the left (\(Q<0\)). Although in the given situation and with the current choice of domain, the flow should be to the right and thus positive, it can not be excluded that during the numerical calculations it may be (locally) different. The friction slope is technically defined as the amount of energy loss if going in the positive x-direction.

  • if the flow is in the positive x-direction (\(Q>0\)), their should be loss in the positive x-direction, so the friction slope should be positive
  • if the flow is in the negative x-direction (\(Q<0\)), their should be loss in the negative x-direction, so the friction slope should be negative

These two cases can be combined into one formula:

\[ S_f = \mathrm{sign}(Q) \frac{n^2\; Q^2}{A^2\; R^{4/3}} \]

where the sign function is defined as in the R-language.

Use the help of R to give a definition of the sign function below:

Using this result, the simplified St-Venant can be written as:

\[ \mathrm{sign}(Q) \; \frac{n^2\; Q^2}{A^2\; R^{4/3}} = S_f = S_o - \frac{\partial a}{\partial x} = -\frac{\partial z_b}{\partial x} - \frac{\partial a}{\partial x} = -\frac{\partial h}{\partial x} \]

Rewriting this gives: \[Q = -\frac{1}{n}\; \mathrm{sign}\Big(\frac{\partial h}{\partial x}\Big)\; \sqrt{ \Big| \frac{\partial h}{\partial x} \Big|}\; A\; R^{2/3}\]

As you can see, the choice of \(h\) as state is convenient here.

The code

HR.Q= function(x,state,gradstate)
{
  a = HR.a(x,state)
  R = HR.R(a)
  A = HR.A(a)
  return(-1/HR.n*sign(gradstate)*sqrt(abs(gradstate))*A*R^(2/3)) #+ (
}

Model 1: the Hooge Raam base model

HR.backwatermodel = newFLOW1D(domain=HR.domain,systemfluxfunction = HR.Q,
                              name = 'Hooge Raam backwater')

HR.weir.BC = function(state)
{
  return(-HR.weirQ(HR.a(HR.length,state)))
}
HR.Qin = 1.2
set.BC.fluxstate(HR.backwatermodel,"right",HR.weir.BC)
set.BC.fixedflux(HR.backwatermodel,where="left","HR.Qin")
set.discretisation(HR.backwatermodel,nodes=seq(0,HR.length,length=50),method='FE')

do.initialize(HR.backwatermodel,HR.bwrough)

HR.abovebottom = function(x,state)
{
  return(HR.a(x,state)>0.1)
}

set.isacceptable(HR.backwatermodel,HR.abovebottom)

Discuss:

  • The minus sign in the definition of the lower boundary condition:
  • use the help of FVFE1D to discuss the role of the double quotes around HR.Qin in the definition of the upstream boundary condition:
  • describe the acceptability condition of the model for the level in words:
solve.steps(HR.backwatermodel)
$RMSM
[1] 1.7876e-06

$MAM
[1] 1.109677e-05
plot(HR.backwatermodel,fluxplot=TRUE)

And a nice plot to compare the rough (linear) approximation with the smooth one:

plot(HR.domain,HR.bwrough(HR.domain),ylim=c(HR.botelev.downstream,HR.botelev.upstream+aequi),
     main=paste("Backwater curve for Q=",HR.Qin),
     xlab="l (m)",ylab="h (m)",col="green",lty=2,lwd=4,type="l")
lines(HR.domain,HR.zb(HR.domain),col="brown",lwd=4)
HR.states.1 = dataframe.states(HR.backwatermodel)
lines(HR.states.1,col="blue",lwd=4)
legend("bottomleft", inset=.05,
       c("bottom","raw backwater","true backwater"), 
       col=c("brown","green","blue"),lty=c(1,2,1),
       lwd=3,horiz=FALSE)

Model 2: adding drainage

All the approaches only considered the Hooge Raam as a transport river: water coming in upstream had to be transported downstream. In reality input (in draining situations) of water does also occur along the trajectory. This is called lateral inflow. In terms of the FVFE1D package this is called spatialflux. In this example we will think of this flux coming from draining the arabel land where water collected in ditches and drain tubes ending up in the river. We will think of this drainage flux to be uniform over the length of the river. In this case this is called HR.lateraldrainage. The name lateral is a typical name for this type of fluxes in open water: lateral means “coming from the side”, so external.

A proper value for this number should come from a groundwater model. For an example value here we just take 50% of the inflow upstream as a total, that has to be distributed over the length of the river.

HR.lateraldrainage = 0.5*HR.Qin/HR.length
add.spatialflux(HR.backwatermodel,rate="HR.lateraldrainage",name="drainage")

solve.steps(HR.backwatermodel,verboselevel = 1)
[1] "iteration 1 ; RMSM= 0.00672594829098378 ; MAM= 0.0412489298613663"
[1] "iteration 2 ; RMSM= 0.000344874286476777 ; MAM= 0.00217852212373981"
[1] "stopped because of small RMSM= 1.38878982277522e-06"
$RMSM
[1] 1.38879e-06

$MAM
[1] 8.655063e-06
HR.states.2 = dataframe.states(HR.backwatermodel)
nodes = HR.states.2$x

plot(nodes,HR.zb(nodes),col="brown",type="l",lwd=4,ylim=c(9,16),
     xlab="l",ylab="h",main=paste("backwater + lateral flow Hooge Raam"))
lines(HR.states.1,col="orange",lwd=3)
lines(HR.states.2,col="blue",lwd=3)
grid(col="black")
legend("bottomleft", inset=.05,
       legend=c("without drainage","with drainage"), col=c("orange","blue"),
       lwd=3,horiz=FALSE)

This concludes the first part and results in the basic open water model simulating flow in the Hooge Raam. For the lateral inflow due to drainage in the surrounding area of the Hooge Raam we chose an arbitrary value. Starting from the next part (2), we will calculated this drainage and also will determine the surface runoff when applicable.

PART 2 The groundwater model

We will assume a 1D groundwater model simulating one specific parcel, enclosed between two ditches or drains, within the drainage area of this part of the Hooge Raam.
This model will calculate a head distribution between both ditches. The model determines how much drainage from the groundwater and discharge from surface runoff (during storm events) will be allocated to the Hooge Raam. This model must be imagined to be perpendicular to the Hooge Raam open water model. Multiplying this length of this model (distance between the two ditches) with a representative ‘width’ to come to the total discharged area.

Basic groundwater equation

Since we will assume a one dimensional model and only horizontal flow (Dupuit) the basic flow equation is:

\[ Q_{gw} = -kD_{gw} * \frac {\partial H}{\partial x} \]

Runoff Cauchy type top boundary condition

\[ Q_{runoff}= \frac{H - H_{surface}}{C_{surface}} \]

Dimensions and parameters groundwater model

The average ditch distance is 125 m and the transmissivity in this area is estimated on 28 \(m^2/d\) based on the hydraulic conductivity of the soil and the thickness.

The average ditch level is given as the average river bottom level + 20 \(cm\).

The entrance resistance, due to sediments at the bottom of the ditch, is 3 \(days\).

When surface runoff occurs, the state exceeds the surface level of 16.0 \(m\) AMSL, a vertical resistance of the upper soil of 20 days (Veranderingsrapportage LHM 3.30 2017, pg 23) can be assumed.

The stationary situation is based on the average yearly recharge; about 0.8 \(mm/d\). For the transient model a situation of a storm event with a maximum precipitation rate of 36 \(mm/hour\) during one hour, will be used as forcing.

Basic setup

Required functions

Finish the functions in the chunck below (replace XXXX, YYYY)

L = 125 # average ditch distance in m

H_drainage = (HR.botelev.upstream + HR.botelev.downstream)/2 + 0.2
GW.kD = 7 * 4 # m2/d #k = 7 m/d and D = 5 m/d

P = 0.0008 #estimated average precipitation m/d

H_surface = 16.0 #average surface level area m
C_surface = 20 #d surface vertical resistance
C_ditch = 3 #d entrance resistance of the ditches

Ditch.bc = function(state)
{
  if(state > H_drainage)
  {
  return((H_drainage - state)/C_ditch)
  }
  else
  {
    return(0)
  }
}

Q.gw = function(x,state,gradstate)
{
  return(-GW.kD * gradstate)
}

Q.srfc = function(x,state)
{
  if(state > H_surface) 
  {
  surface.runoff = (state - H_surface)/C_surface
  return(-surface.runoff)
  }
  else
  {  
    return(0)
  }
}

Basic model

Finish the code in the chunk below (replace XXXX) You may chose which type of discretisation you want to use (FE, FV etc.)

GW.domain = c(0,L)
GW.nodes = seq(GW.domain[1],GW.domain[2],length.out =40)
GW.stat = newFLOW1D(domain = GW.domain,systemfluxfunction = Q.gw, name = "groundwater model")
add.spatialflux(model = GW.stat,rate = "P",name = "precipitation" )
add.spatialflux(model = GW.stat,rate = Q.srfc,name = "surface_runoff")

set.discretisation(model = GW.stat,GW.nodes,method = "FE")
set.BC.fluxstate(model = GW.stat,where = "left",func = Ditch.bc)
set.BC.fluxstate(model = GW.stat, where = "right",func = Ditch.bc)

do.initialize(model = GW.stat,H_surface)
summary(GW.stat)
one dimensional flow model
name:  groundwater model 
model has 2 spatial flux, with name
 surface_runoff precipitation
model has no point fluxes
left BC is of type state-flux 
right BC is of type state-flux 

numerical model of type FElinear1D 
number of nodes is  40 
solve.steps(GW.stat, verboselevel = 1)
[1] "iteration 1 ; RMSM= 0.0381387758809951 ; MAM= 0.0805878718255372"
[1] "stopped because of small RMSM= 1.42465493486623e-11"
$RMSM
[1] 1.424655e-11

$MAM
[1] 4.291004e-11
plot(GW.stat,fluxplot=T)

wbal = dataframe.balance(GW.stat)
knitr:: kable(wbal) #creates a table in the html
name inregion outregion net
internal 0.0 0.0 0.0
surface_runoff 0.0 0.0 0.0
precipitation 0.1 0.0 0.1
boundary 0.0 0.1 -0.1
sum 0.1 0.1 0.0
NA

Transient simulations

The water balance for the transient groundwater model can be given as:

\[ Q_{storage}= Q_{internal} + Q_{external}\\ S \frac{\partial H}{\partial t} = \frac {\partial}{\partial x}\left ( -kD \frac{\partial H}{\partial x} \right ) + \sum Q_i \]

The Internal and External fluxes are already implemented and now we are going to include the transient part, see also the second assignment with FVFE in week two.

The storage coefficient is estimated on 5%. For the storm events, the time step is set to 1 hour and the total simulation time is 10 days, starting with a stationary situation, followed by a storm event of a gradually increasing precipitation rate till 36 mm/hour and gradually decreasing again during 24 hours.

A time loop is required for this simulation stepping through the hourly time steps.

Also the previous state (head) is required and need to be stored temporarily to determine the storage change. Tip: use the ‘specific’ function in the FVFE1D package for for this.

Storage function

A new “external” flux for the storage now need to be defined;

Since the time derivative is \(\frac{\partial H}{\partial t} \approx \frac {H^{t+\Delta t}-H^t}{\Delta t}\) water is stored, and not available for internal flow, when the new head is higher than the previous head.

Finish the Q.sto function (replace XXXX, YYYY)

old.state = state.fun(model = GW.stat) #a FUNCTION (!) to save the previous state of the model, so H at t= t to calculate the new H at t = t + delt.t .
GW.S = 0.05
Q.sto = function(x,state)
{
  Storage.flux = - GW.S * (state - old.state(x))/delt.t
  return(Storage.flux)
}

Transient model

The transient model is the same as the stationary model with the addition of the storage flux.

GW.trans = copy.model(GW.stat)
add.spatialflux(model = GW.trans,rate = Q.sto,name = "storage")
summary(GW.trans)
one dimensional flow model
name:  groundwater model 
model has 3 spatial flux, with name
 surface_runoff storage precipitation
model has no point fluxes
left BC is of type state-flux 
right BC is of type state-flux 

numerical model of type FElinear1D 
number of nodes is  40 

Storm event

The storm event is simulated within one day (24 hours) with a max of 360 mm/d during one hour half way this day.

P.pattern.rate = c(0.0008,0.015*24*(sin(pi*rep(1/24*(1:24)))),0.0008)
P.pattern.date = c(0,rep(1/24*(1:24)),(1+1/24))
P.pattern = approxfun(P.pattern.date,P.pattern.rate,rule = 2)
plot(P.pattern.date,P.pattern(P.pattern.date),type="l",col = "blue",
     lwd = 2, xlab = "days",ylab = "intinsity in m/d",main = "Storm event in one day")
grid()

Time loop

Finish the time loop replacing XXXX, YYYY, ZZZZ and AAAA.


#data containers for results
GW.Q.trans = c()
GW.H.trans = c()

#using the stationary head of the stationary model for the initial head for the first time step for the transient model
old.state = state.fun(GW.trans) # first initial old state to be used in the while loop.

#time aspects
delt.t = 1/24 # 1 hour
start.time = 0
end.time =  10.0 #days

current.time = start.time

#time loop
while (current.time < end.time)
{
  current.time = current.time + delt.t
  P = P.pattern(current.time) #precipitation during the storm 
  solve.steps(GW.trans)
  #save intermediate data
  wb = dataframe.balance(GW.trans)
  GW.Q.trans = rbind(GW.Q.trans, c(current.time,wb[2,3],wb[3,2],wb[3,3],wb[4,2],wb[5,3]))
  GW.H.trans = rbind(GW.H.trans, dataframe.states(GW.trans)[,2])
  #save current state being the old state for the next time step
  old.state = state.fun(GW.trans)
}
  #storing and plotting results
  GW.Q.trans = data.frame(GW.Q.trans)
  colnames(GW.Q.trans) = c("time", "surface_runoff","storage_2flow","storage_2storage","precip","boundary")
  
  #plot some results
  Q.range = range(GW.Q.trans$surface_runoff,GW.Q.trans$storage_2flow,GW.Q.trans$storage_2storage,GW.Q.trans$precip,GW.Q.trans$boundary)
  plot(GW.Q.trans$time,GW.Q.trans$surface_runoff,ylim = Q.range, type = "l", col = "red",lwd =2,
       ylab = "flux rates in m2/d", xlab = "time in days", main = "flux rates groundwater model")
  lines(GW.Q.trans$time,GW.Q.trans$storage_2flow,col="yellow4",lwd = 2)
  lines(GW.Q.trans$time,GW.Q.trans$storage_2storage,col="orange",lwd = 2)
  lines(GW.Q.trans$time,GW.Q.trans$precip,col="blue", lwd = 2)
  lines(GW.Q.trans$time,GW.Q.trans$boundary,col="green")
  legend("topright", inset=.05,
       legend=c("runoff","storage_2flow","storage_2storage","precipitation","drainage"), col=c("red","yellow4","orange","blue","green"),
       lwd=3,horiz=FALSE)

#first 2 days 2*24 = 48 row
  
  plot(GW.Q.trans$time[1:48],GW.Q.trans$surface_runoff[1:48],ylim = Q.range, type = "o", col = "red",lwd =2,
       ylab = "flux rates in m2/d", xlab = "time in days", main = "flux rates groundwater model")
  lines(GW.Q.trans$time[1:48],GW.Q.trans$storage_2flow[1:48],col="yellow4",lwd = 2)
  lines(GW.Q.trans$time[1:48],GW.Q.trans$storage_2storage[1:48],col="orange",lwd = 2)
  lines(GW.Q.trans$time[1:48],GW.Q.trans$precip[1:48],col="blue", lwd = 2)
  lines(GW.Q.trans$time[1:48],GW.Q.trans$boundary[1:48],col="green", lwd = 2)
  legend("topright", inset=.05,
       legend=c("runoff","flow over (saturated)storage","flow to (unsaturated) storage","precipitation","drainage"), col=c("red","yellow4","orange","blue","green"),
       lwd=3,horiz=FALSE)
grid()  


GW.Q.data = summary(GW.Q.trans)  
knitr::kable(GW.Q.data, caption = "summary flow budget groundwater model in m2/d", format = "simple")
summary flow budget groundwater model in m2/d
time surface_runoff storage_2flow storage_2storage precip boundary
Min. : 0.04167 Min. :0.0000 Min. : 0.0000 Min. : 0.000 Min. : 0.000 Min. :0.1243
1st Qu.: 2.54167 1st Qu.:0.0000 1st Qu.: 0.7982 1st Qu.: 0.000 1st Qu.: 0.100 1st Qu.:0.9386
Median : 5.04167 Median :0.0000 Median : 0.9869 Median : 0.000 Median : 0.100 Median :1.1340
Mean : 5.04167 Mean :0.8901 Mean : 1.6169 Mean : 2.437 Mean : 2.939 Mean :1.2288
3rd Qu.: 7.54167 3rd Qu.:0.2717 3rd Qu.: 1.3105 3rd Qu.: 0.000 3rd Qu.: 0.100 3rd Qu.:1.4251
Max. :10.04167 Max. :9.0618 Max. :10.6947 Max. :43.474 Max. :45.000 Max. :2.3883

The term storage_2flow stands for storage which is released to flow and storage_2storage is the amount of water which will be stored.

PART 3 Coupling open water and groundwater

A “loose” coupling will be applied and analyzed. With this the open water model will receive drainage and surface runoff fluxes from the groundwater model.
Later on, one may use the calculated open water level to adjust the drain depth in the groundwater model achieving a full coupling. This will result in a full coupling: exchanging fluxes from groundwater to open water and drain depth from open water to groundwater

Scaling of Drainage and surface runoff

The pre-calculated drainage and surface runoff of the groundwater model are used as input (“forcing”) for the open water model HR.backwatermodel and need to be “scaled” to proper dimensions; \(m^3/s\).
The groundwater drainage and surface runoff fluxes are based on one typical parcel having an average ditch distance of 125 m. There are many of these parcels in the catchment area of the Hooge Raam. These discharge rates have unit \(m^2/d\) and need to be transformed to the total discharging area of the Hooge Raam into units of \(m^3/s\). For this, you can imagine all parcels, with an average ditch distance of 125m, were set behind each other resulting in a, let’s call it a diffuse length calculated as: \(\frac {\text{total_drained_area}}{\text{ditch_distance}}\). The figure below illustrates the determination of the total diffuse length = \({\sum\text{LocalPathLength}}=\frac{\text{Drained Area}}{\text{Ditch Distance}}\)
illustration to indicate how the diffuse length is determined
Finally the groundwater drainage and surface runoff need to be transformed to a lateral inflow rate of \(m^3/s/m_{HRcourse}\) for the Hooge Raam.
The total considered discharging area of the Hooge Raam is 500 ha.

Determine the properly scaled drainage and runoff for the Hooge Raam model ; HR.backwatermodel.

## from groundwater discharge to lateral inflow into the Hooge Raam open water model
HR.discharge.area = 500 * 10^4#in m2
day2sec = 86400 #from day to seconds
m2.day.to.m3.day =   HR.discharge.area/L #from m2/day to m3/day; a "diffuse length" based on the total drained area divided by the L(ditch distance); unit is m because it is used to convert m2/day to m3/day

Q.drainage.m3.d = GW.Q.trans$boundary * m2.day.to.m3.day # Total drainage in the catchment
Q.drainage.m3.s = Q.drainage.m3.d/day2sec# Total drainage in the catchment
Q.drainage.m2.s = Q.drainage.m3.s/HR.length
  #Drainage per m river

Q.runoff.m3.d = GW.Q.trans$surface_runoff * m2.day.to.m3.day # Total runoff in the catchment
Q.runoff.m3.s = Q.runoff.m3.d/day2sec # Total runoff in the catchment
Q.runoff.m2.s = Q.runoff.m3.s/HR.length # Runoff per m river

Closely inspect the following chunk running the open water model HR.backwatermodel with drainage and surface fluxes coming from the groundwater model.


####preparing open water model for drainage and surface runoff
summary(HR.backwatermodel)
plot(HR.backwatermodel,fluxplot = T)

##removing the old lateral flux and adding the new drainage and runoff lateral fluxes
rem.spatialflux(HR.backwatermodel,name = "drainage")
add.spatialflux(HR.backwatermodel,rate = "drainage",name = "drainage")
add.spatialflux(HR.backwatermodel,rate = "runoff", name = "runoff")
summary(HR.backwatermodel)


#data containers for results
HR.Q.trans = c()
HR.h.trans = c()
HR.Q_upstream = c()
HR.Q_downstream = c()
#data container for results

#plot facility
plot(nodes,HR.zb(nodes),col="brown",type="l",lwd=4,ylim=c(11,18),
     xlab="l",ylab="h",main=paste("Transient water levels Hooge Raam"))
lines(dataframe.states(HR.backwatermodel), col = "blue", lwd = 3)
grid()

#delt.t = 1/24 comes from groundwater model
current.time = 0.0
#end.time = 2 comes from groundwater model
time.step = 0

####time loop for HR.backwatermodel with fluxes from the groundwater model
while(current.time < end.time)

{
  current.time = current.time + delt.t
  time.step = time.step + 1
  drainage = Q.drainage.m2.s[time.step]
  runoff = Q.runoff.m2.s[time.step]
  solve.steps(HR.backwatermodel)
  ##saving intermediate results
  wb = dataframe.balance(HR.backwatermodel)
  HR.Q.trans = rbind(HR.Q.trans, c(current.time,wb[4,2],wb[3,2],wb[2,2],wb[4,3]))
  HR.h.trans = rbind(HR.h.trans, dataframe.states(HR.backwatermodel)[,2])
  
  #create plot to generate an animation in the knitted HTML document
  plot(nodes,HR.zb(nodes),col="brown",type="l",lwd=4,ylim=c(11,18),
       xlab="l",ylab="h",main=paste("Transient water levels Hooge Raam"))
  lines(dataframe.states(HR.backwatermodel), col = "blue", lwd = 3)
  grid()
  
}
  ## creating data frames for the result of the open water model with drainage and surface runoff 
 HR.Q.trans = data.frame(HR.Q.trans)
 colnames(HR.Q.trans) = c("time","boundary_upstream","runoff","drainage","boundary_downstream")
 HR.h.trans = data.frame(HR.h.trans)

Checking fluxes and total volumes

For both the transient groundwater and the pseudo-stationary open water model the total volumes of water (in \(m^3\)) during the simulation time are compared with both models.

groundwater

Volume balance for the groundwater model \[ Total_{in} = \sum_{t= 0}^{t=T}Precip(t)\Delta t\\ Total_{out} = \left \{ \sum_{t=0}^{t=T} Qsto(t)+\sum_{t=0}^{t=T} Runoff(t) + \sum_{t=0}^{t=T} Drainage(t) \right \} \Delta t \] Recall that the water balance of the groundwater model is expressed in \(m^2/d\). To come to volumes (\(m^3\)) one need to consider the “diffuse length” and the time step.


####preparing open water model for drainage and surface runoff
summary(HR.backwatermodel)
one dimensional flow model
name:  Hooge Raam backwater 
model has 1 spatial flux, with name
 drainage
model has no point fluxes
left BC is of type given flux 
right BC is of type state-flux 

numerical model of type FElinear1D 
number of nodes is  50 
plot(HR.backwatermodel,fluxplot = T)


##removing the old lateral flux and adding the new drainage and runoff lateral fluxes
rem.spatialflux(HR.backwatermodel,name = "drainage")
add.spatialflux(HR.backwatermodel,rate = "drainage",name = "drainage")
add.spatialflux(HR.backwatermodel,rate = "runoff", name = "runoff")
summary(HR.backwatermodel)
one dimensional flow model
name:  Hooge Raam backwater 
model has 2 spatial flux, with name
 drainage runoff
model has no point fluxes
left BC is of type given flux 
right BC is of type state-flux 

numerical model of type FElinear1D 
number of nodes is  50 
#data containers for results
HR.Q.trans = c()
HR.h.trans = c()
HR.Q_upstream = c()
HR.Q_downstream = c()
#data container for results

#plot facility
plot(nodes,HR.zb(nodes),col="brown",type="l",lwd=4,ylim=c(11,18),
     xlab="l",ylab="h",main=paste("Transient water levels Hooge Raam"))
lines(dataframe.states(HR.backwatermodel), col = "blue", lwd = 3)
grid()


#delt.t = 1/24 comes from groundwater model
current.time = 0.0
#end.time = 2 comes from groundwater model
time.step = 0

####time loop for HR.backwatermodel with fluxes from the groundwater model
while(current.time < end.time)

{
  current.time = current.time + delt.t
  time.step = time.step + 1
  drainage = Q.drainage.m2.s[time.step]
  runoff = Q.runoff.m2.s[time.step]
  solve.steps(HR.backwatermodel)
  ##saving intermediate results
  wb = dataframe.balance(HR.backwatermodel)
  HR.Q.trans = rbind(HR.Q.trans, c(current.time,wb[4,2],wb[3,2],wb[2,2],wb[4,3]))
  HR.h.trans = rbind(HR.h.trans, dataframe.states(HR.backwatermodel)[,2])
  
  #create plot to generate an animation in the knitted HTML document
  plot(nodes,HR.zb(nodes),col="brown",type="l",lwd=4,ylim=c(11,18),
       xlab="l",ylab="h",main=paste("Transient water levels Hooge Raam"))
  lines(dataframe.states(HR.backwatermodel), col = "blue", lwd = 3)
  grid()
  
}

  ## creating data frames for the result of the open water model with drainage and surface runoff 
 HR.Q.trans = data.frame(HR.Q.trans)
 colnames(HR.Q.trans) = c("time","boundary_upstream","runoff","drainage","boundary_downstream")
 HR.h.trans = data.frame(HR.h.trans)

open water

\[ Total_{in} = \sum_{t=0}^{t=T} boundary_{upstream}\Delta t + \sum_{t=0}^{t=T} runoff \Delta t + \sum_{t=0}^{t=T} drainage \Delta t\\ Total_{out} = \sum_{t=0}^{t=T} boundary_{downstream} \]

# Water volumes for the 1D groundwater model during the total simulation time

GW.volume.drainage = sum(GW.Q.trans$boundary) * m2.day.to.m3.day * delt.t
GW.volume.runoff = sum(GW.Q.trans$surface_runoff) * m2.day.to.m3.day * delt.t

GW.volume.storage_into = sum(GW.Q.trans$storage_2flow) * m2.day.to.m3.day * delt.t
GW.volume.storage_outof = sum(GW.Q.trans$storage_2storage) * m2.day.to.m3.day * delt.t

GW.volume.precipitation = sum(GW.Q.trans$precip) * m2.day.to.m3.day * delt.t

GW.misfit = GW.volume.precipitation - GW.volume.drainage - GW.volume.runoff - GW.volume.storage_outof + GW.volume.storage_into

volume.bal.GW = data.frame(Groundwater = c("precipitation","storage_2flow","drainage","runoff","storage_2storage","total"),
                        Volume_in = c(GW.volume.precipitation,GW.volume.storage_into,0,0,0,sum(GW.volume.precipitation,GW.volume.storage_into)),
                        Volume_out = c(0,0,GW.volume.drainage,GW.volume.runoff,GW.volume.storage_outof,sum(GW.volume.drainage,GW.volume.runoff,GW.volume.storage_outof)))#,
                        
knitr::kable(volume.bal.GW,format = "simple", caption = "Total volumes in m^3 groundwater model")
Total volumes in m^3 groundwater model
Groundwater Volume_in Volume_out
precipitation 1180445.5 0.0
storage_2flow 649463.1 0.0
drainage 0.0 493572.9
runoff 0.0 357520.4
storage_2storage 0.0 978815.7
total 1829908.6 1829909.0
NA

Check whether the output of the groundwater are the same inputs for the open water model.

Time series analysis

It can be interesting to for example compare time series for the precipitation and discharge of the Hooge Raam. With this, one can examine the response of a storm event on the open water - groundwater system.

# water volumes for the 1D open water (Hooge Raam) model during the total simulation time
HR.volume.bnd.upstream = sum(HR.Q.trans$boundary_upstream) * delt.t * 86400 #unit delt.t is in days!
HR.volume.runoff = sum(HR.Q.trans$runoff) * delt.t * 86400
HR.volume.drainage = sum(HR.Q.trans$drainage) * delt.t * 86400
HR.volume.bnd.downstream = sum(HR.Q.trans$boundary_downstream) * delt.t * 86400

HR.volume.in = HR.volume.bnd.upstream + HR.volume.runoff + HR.volume.drainage
HR.volume.out = HR.volume.bnd.downstream


## this should add up to the total amount of precipitation during the simulation time.

volume.bal.HR = data.frame(Open_water = c("inflow_upstream","surface_runoff","drainage","outflow_downstream","total"),
                           Volume_in = c(HR.volume.bnd.upstream,HR.volume.runoff,HR.volume.drainage,0,HR.volume.in),
                           Volume_out = c(0,0,0,HR.volume.bnd.downstream,HR.volume.out))
knitr::kable(volume.bal.HR,format.arg = list(digits = 8,nsmall = 3), caption = "Total volumes in m^3 open water model") #,digits = 15)
Total volumes in m^3 open water model
Open_water Volume_in Volume_out
inflow_upstream 1041120.000 0.000
surface_runoff 357520.380 0.000
drainage 493572.913 0.000
outflow_downstream 0.000 1892213.340
total 1892213.293 1892213.340
NA

Zooming in for the first two days.

plot(GW.Q.trans$time, P.pattern(GW.Q.trans$time), col = "blue", lwd =2, ylab = "m/d",xlab = "days",type= "l", main = "Transient fluxes open water model in m/d", sub = "Total simulation time")
HR.discharge.m.d = HR.Q.trans$boundary_downstream * 86400 / HR.discharge.area
lines(HR.Q.trans$time,HR.discharge.m.d,col = "red", lwd =2)
HR.runoff.m.d = HR.Q.trans$runoff * 86400 / HR.discharge.area
lines(HR.Q.trans$time,HR.runoff.m.d, col = "brown",lwd =  2)
HR.drainage.m.d = HR.Q.trans$drainage * 86400 / HR.discharge.area
lines(HR.Q.trans$time,HR.drainage.m.d, col = "green",lwd =2)
grid()
legend("topright", inset=.05,
       legend=c("precipitation","HR discharge","runoff","drainage"), col=c("blue","red","brown","green"),
       lwd=3,horiz=FALSE)

time.max.precip = which.max(P.pattern(GW.Q.trans$time))*delt.t
abline(v=time.max.precip, col = "blue")

time.max.discharge = which.max(HR.Q.trans$boundary_downstream)*delt.t
time.max.runoff = which.max(HR.Q.trans$runoff)*delt.t
time.max.drainage = which.max(HR.Q.trans$drainage)*delt.t

abline(v=time.max.discharge,col='red',lwd=3)
abline(v=time.max.drainage, col = "green")

From the graphs it’s clearly visible that the discharge is delayed compared to the precipitation. The max. precipitation is at day time.max.precip (blue vertical line) and the max. discharge is at day : time.max.discharge (red vertical line)

The moment when the maximum discharge at the weir of the Hooge Raam is reached coincides with the max. runoff. Why? ANSWER: Runoff and drainage both come from the same 1D groundwater model. Since this model is 1 dimensional, from ditch to ditch, it does not contain a spatial distribution with parcel far away and close by the Hooge Raam. Moreover the 1D transient groundwater model calculates the effect of precipitation on groundwater storage, drainage and surface runoff in the same time step. In other words, precipitation is distributed over these three groundwater sinks in the same time.

PART 4 Sensitivity analysis

Here we will consider the local sensitivity analysis.

The task is to determine what the uncertainty of a model (derived) result is due to the used the parameter values in the model.

For example; suppose one want’s to determine the uncertainty of the max head in a groundwater model, due to the model parameters (like kD, boundary-conditions, precipitation, for which you not have exact, or good values.

For this the following aspects need to be considered/determined:

  1. derived result, like the max discharge (in time) of the Hooge Raam

  2. list of parameters and it optimal (assumed) values

  3. list of the scale of variation of the parameters

  4. local sensitivities for the parameters

  5. scaled contributions to uncertainty of each parameter

Derived result

all results of the current coupled models:

groundwater model:

  1. heads, \(H\), at different time steps and locations

  2. water balance terms(for different time steps and locations:

    1. boundary in, boundary out (drainage)

    2. internal flux

    3. storage

    4. precipitation

    5. surface runoff

The max discharge is HR.Q.trans$boundary_downstream[time.max.discharge] in \(m^3/s\) and takes place at time : HR.Q.trans$time[time.max.discharge] days.

open water model:

  1. water levels at different time steps and locations

  2. water balance terms (for different time steps and locations)

    1. influx upstream

    2. outflux downstream

Parameter list

All parameters can be used for analysis of the uncertainty of the model result, of both, models.

groundwater model:

  1. Transmissivity of the subsoil GW.kD

  2. Entrance resistance of the ditch C_ditch

  3. Average drain level of the area H_drainage

  4. Vertical resistance top layer soil C_surface

  5. Average surface level drainage system H_surface

  6. Storage coefficient; GW.S

open water model:

  1. value weir constant (currently 1.83)

  2. Weir crest height HR.weir.crest

  3. Weir width HR.weir.width

  4. Manning coefficient HR.n

  5. River width HR.b

  6. Side slopes HR.m

Scale of variation (\(\sigma\))

The scale of variation could be based on literature, field experiments or for example on a model calibration.

Calculation local sensitivities

After we have determined the base model result, we will determine the local sensitivity of the base result w.r.t. parameter; \(\frac{\partial Model_{result}}{\partial P_i}\).

Scaled contributions to result uncertainty

ASSIGNMENT

Carry out a local sensitivity analysis for one of the model results, the groundwater and open water model.

  1. Choose at least 6 parameters, preferably from both models, to carry out the sensitivity analysis with
  2. Estimate the scale of variation of these parameters, based on your experience, what you have heard during courses or simply google it
  3. Create chunks to determine the local sensitivities
  4. Make use of the pre-programmed sensitivity.loop() executing time loops for each model and saving intermediate data
  5. Create a chunk to calculate the derived uncertainties

This sensitivity.loop() contains many lines of code but basically does the following:

  1. Run the transient groundwater model
  2. Store results, flux rates (m2/d) and heads, of the groundwater model
  3. Multiply the drainage and runoff flux rates with the diffuse length to come to m3/d and then to m3/s
  4. Run the transient open water model with the current drainage and runoff flux rates
  5. Store the open water flux rates and open water levels

To set up a proper procedure, have close look at the Local Sensitivity Analysis assignment 1, Local sensitivity 1D.

Derived uncertainty to analyse; the MRESULT()

For a waterboard it is important to have some insight in the way storm events are processed within the area they control. It is therefor important that the water managers can take measures beforehand, like lowering the weir of the Hooge Raam before the storm event arrives.

In this example the derived uncertainty will be the the max. weir discharge (downstream of the Hooge Raam).

There are several interesting model results for close inspection w.r.t. their ‘derived’ uncertainties. To give some examples:

  • Moment when the water levels of the Hooge Raam rises fastest
  • The buffer capacity of the area, e.g. the moment when most of the precipitation is stored in the ground
  • The max. water level in the Hooge Raam
  • Effect of the precipitation distribution during a storm event.

All results are stored in the following “containers” which are all simple data frame. Simply click on them in the upper right panel (Environmental tab) to what’s all stored;

  • GW.Q.trans for all flux rates of the groundwater model in m2/d; each row is one time step
  • GW.H.trans for all heads in the groundwater model; each row is one time step
  • HR.Q.trans for all flux rates in the open water model in m3/s; each row is one time step
  • HR.H.trans for all heads in the open water model; each row is one time step

As an example, MRESULT() is the max. discharge of the Hooge Raam at the weir.

plot.time = GW.Q.trans$time[1:48]
plot(plot.time, P.pattern(plot.time), col = "blue", lwd =2, ylab = "m/d",xlab = "days",type= "o", main = "Transient fluxes open water model in m/d", sub = "First two days simulation time")
HR.discharge.m.d = HR.Q.trans$boundary_downstream * 86400 / HR.discharge.area
lines(HR.Q.trans$time,HR.discharge.m.d,col = "red", lwd =2)
HR.runoff.m.d = HR.Q.trans$runoff * 86400 / HR.discharge.area
lines(HR.Q.trans$time,HR.runoff.m.d, col = "brown",lwd =  2)
HR.drainage.m.d = HR.Q.trans$drainage * 86400 / HR.discharge.area
lines(HR.Q.trans$time,HR.drainage.m.d, col = "green",lwd =2)
grid()
legend("topright", inset=.05,
       legend=c("precipitation","HR discharge","runoff","drainage"), col=c("blue","red","brown","green"),
       lwd=3,horiz=FALSE)

time.max.precip = which.max(P.pattern(plot.time))*delt.t
abline(v=time.max.precip, col = "blue")

time.max.discharge = which.max(HR.Q.trans$boundary_downstream)*delt.t
time.max.runoff = which.max(HR.Q.trans$runoff)*delt.t
time.max.drainage = which.max(HR.Q.trans$drainage)*delt.t

abline(v=time.max.discharge,col='red',lwd=1)
abline(v=time.max.drainage, col = "green")

Be aware that MRESULT() is based on the results which are contained in the current data frames GW.Q.trans, QW.H.trans, HR.Q.trans, HR.H.trans. The original value of MRESULT() is assigned to M_base, in the code chunk of the following section “Base run”.

Model parameters

As an example the following model parameters can be used for the analysis;

  1. HR.n the manning coefficient
  2. C_ditch the entrance resistance of the ditches in the area
  3. HR.Qin the influx in \(m^3/s\) at the upstream end of the Hooge Raam
  4. H_surface the average elevation of the drained area
  5. H_drainage the average drain level of the drained area
  6. GW.S storage coefficient of the subsoil (replacement of 5)

Set up the base list for the aforementioned parameters. You may also choose different parameters right away.

MRESULT = function(n)
{
  if(n == 1)#maximum discharge of the Hooge Raam at the weir
  {
    max.discharge= max(HR.Q.trans$boundary_downstream)
    return(max.discharge)
  }
  else if(n == 2)#Max head of groundwater
  {
    max.head= max(GW.H.trans)
    return(max.head)
  }
  else if(n == 3)#Max water height at Hooge Raam
  {
    max.height= max(HR.h.trans$X50)
    return(max.height)
  }
  else if(n == 4) #Max drainage of the groundwater
  {
    max.drain.HR= max(GW.Q.trans$boundary)
    return(max.drain.HR)
  }
  else if(n == 5) #Lag of maximum precipitation and the maximum head of groundwater
  {
    maximum_head = max(GW.H.trans)
    eps = 10^-7
    index = c(0,0)
    dimension = dim(GW.H.trans)
    for(i in (1:dim(GW.H.trans)[2]))
    {
      for(j in (1:dim(GW.H.trans)[1]))
      {
        if(abs(maximum_head - GW.H.trans[j,i])<eps) #compare if GW.H.trans now == what we search
        {
          index = c(i,j) # i is column, j is row, if it is equal take the index
        }
      }
    }
    
    maximum_precipitation = max(GW.Q.trans$precip)
    indexp = c(0,0)
    for(i in (1:length(GW.Q.trans$precip)))
    {
        if(abs(maximum_precipitation - GW.Q.trans$precip[i])<eps) #compare if GW.Q.trans now == what we search
        {
          indexp = c(i) # if it is equal take the index
        }
    }
    
    return((index[2]-indexp)*delt.t)
  }
}

Scale of variation

Estimate the scale of variaton for the model parameters.

base = list(GW.kD = GW.kD, C_ditch = C_ditch, H_drainage = H_drainage, C_surface = C_surface, H_surface = H_surface, GW.S = GW.S, HR.weir.crest = HR.weir.crest, HR.weir.width = HR.weir.width, HR.n = HR.n, HR.b = HR.b, HR.m = HR.m) # the list with the current model parameters
str(base)# print the list for inspection
List of 11
 $ GW.kD        : num 28
 $ C_ditch      : num 3
 $ H_drainage   : num 13.3
 $ C_surface    : num 20
 $ H_surface    : num 16
 $ GW.S         : num 0.05
 $ HR.weir.crest: num 1
 $ HR.weir.width: num 2.25
 $ HR.n         : num 0.045
 $ HR.b         : num 2.1
 $ HR.m         : num 1.5

Base run

To determine the sensitivities the base data need to be saved;

scale = list(GW.kD = 0.5*GW.kD, C_ditch = 0.6*C_ditch, H_drainage = 0.3, C_surface = 0.6*C_surface, H_surface = 0.3, GW.S = 0.4*GW.S, HR.weir.crest = 0.10, HR.weir.width = 0.05, HR.n = 0.25*HR.n, HR.b = 0.3, HR.m = 0.2*HR.m)#list of the scale of variation of the model parameters
str(scale)#print the scales for inspection
List of 11
 $ GW.kD        : num 14
 $ C_ditch      : num 1.8
 $ H_drainage   : num 0.3
 $ C_surface    : num 12
 $ H_surface    : num 0.3
 $ GW.S         : num 0.02
 $ HR.weir.crest: num 0.1
 $ HR.weir.width: num 0.05
 $ HR.n         : num 0.0112
 $ HR.b         : num 0.3
 $ HR.m         : num 0.3

Determine local parameter senitivities

The chosen parameters will be altered by the eps value and reset after the run.
Next the local sensitivity of this parameter \(P_i\) w.r.t. MRESULT() will be determined; \(\frac{\partial \text{MRESULT}}{\partial P_i}\).

The sensitity loop function

To automate the procedure to determine the local sensitivities we will make use of a function running the groundwater model loop and the open water model loop in a sequence; the sensitivity.loop() function;

Closely inspect what is carried out during one pass of this loop.
Probably not a very familiar operator in this loop is the <<- symbol/operator. It means that global P, drainage, runoff, GW.Q.trans GW.H.trans HR.Q.trans HR.h.trans data.frames and the old.state function are adjusted within this function avoiding a very large set of arguments to pass to this function.

M_base = MRESULT(1)
print(M_base)
[1] 6.49736
GW.state.base = GW.H.trans# a data.frame for the heads per time step of the groundwater model
GW.flux.base = GW.Q.trans# a data.frame for the fluxes per time step of the groundwater model
HR.state.base = HR.h.trans# a data.frame for the water levels of the Hooge Raam model
HR.flux.base = HR.Q.trans# a dataa.frame for the fluxes per time step of the Hooge Raam model

eps = 0.01 #1% of the original value hopefully avoiding rounding errors and still be in de proper local domain.

Local sensitivities

Each local sensitivity can now be determined using a sequence of:

  • adding eps to the current parameter \(P_i\)
  • running the Sensitivity.loop()
  • calculating \(\frac{\partial \text{MRESULT()}}{\partial P_i}\)
  • subtracting eps from the current parameter.

Set up the sensitivity sequence, see above, to determine the local sensitivities for the chosen parameters.

TIP: you can use eps being an offset so, \(P_i = P_i + eps\) or use it as a fraction: \(P_i = P_i*(1 + eps)\)

Sensitivity.loop = function()
{
  # the groundwater model loop, GW model first because we use the drainage and runoff to the backwater model, after that run backwater model
  
  #data container for results
  GW.Q.trans = c()
  GW.H.trans = c()
  temp.runoff = c()
  

  #using the stationary head for the initial head for the first time step for the transient model
  old.state <<- state.fun(GW.stat)
  
  delt.t = 1/24 # 1 hour
  start.time = 0
  end.time =  10.0
  
  current.time = start.time
  
  while (current.time < end.time)
  {
    #time stepping
    current.time = current.time + delt.t
    
    #Groundwater model run
    P <<- P.pattern(current.time)  #should be a global variable, hence <<-, for the GW.trans model
    solve.steps(GW.trans)
    #  plot(GW.trans,fluxplot = T)
    #save intermediate data
    wb = dataframe.balance(GW.trans)
    GW.Q.trans = rbind(GW.Q.trans, c(current.time,wb[2,3],wb[3,2],wb[3,3],wb[4,2],wb[5,3]))
    GW.H.trans = rbind(GW.H.trans, dataframe.states(GW.trans)[,2])
    #save intermediate data  
    old.state <<- state.fun(GW.trans)
  }
  #browser()
  # Storing data 
  GW.Q.trans = data.frame(GW.Q.trans)
  colnames(GW.Q.trans) = c("time", "surface_runoff","storage_in","storage_out","precip","boundary")
  GW.H.trans = data.frame(GW.H.trans)
  
  # Scaling the groundwater fluxes to lateral influxes for the open water model
  Q.drainage.m3.d = GW.Q.trans$boundary * m2.day.to.m3.day
  Q.drainage.m3.s = Q.drainage.m3.d/day2sec
  Q.drainage.m2.s = Q.drainage.m3.s/HR.length
  
  Q.runoff.m3.d = GW.Q.trans$surface_runoff * m2.day.to.m3.day
  Q.runoff.m3.s = Q.runoff.m3.d/day2sec
  Q.runoff.m2.s = Q.runoff.m3.s/HR.length
  
  
  ## the open water model loop
  
  
  #data containers for results
  HR.Q.trans = c()
  HR.h.trans = c()
  HR.Q_upstream = c()
  HR.Q_downstream = c()
  #data container for results
  
  #time steps for the loop
  current.time = start.time
  #time step for retrieving data from drainage and surface time series of the groundwater model
  time.step =start.time
  while(current.time < end.time)
    
  {
    current.time = current.time + delt.t
    time.step = time.step + 1
    drainage <<- Q.drainage.m2.s[time.step]  #global variable for global HR.backwatermodel
    runoff <<- Q.runoff.m2.s[time.step]  #global variable for global HR.backwatermodel
    
    solve.steps(HR.backwatermodel)
    ##saving intermediate results
    wb = dataframe.balance(HR.backwatermodel)
    HR.Q.trans = rbind(HR.Q.trans, c(current.time,wb[4,2],wb[3,2],wb[2,2],wb[4,3]))
    HR.h.trans = rbind(HR.h.trans, dataframe.states(HR.backwatermodel)[,2])
    ##saving intermediate results
  }
  #browser()
  HR.Q.trans = data.frame(HR.Q.trans)
  colnames(HR.Q.trans) = c("time","boundary_upstream","runoff","drainage","boundary_downstream")
  HR.h.trans = data.frame(HR.h.trans)
  #returning the new data.frames to the global environment for further analysis.
  GW.Q.trans <<- GW.Q.trans
  GW.H.trans <<- GW.H.trans
  HR.Q.trans <<- HR.Q.trans
  HR.h.trans <<- HR.h.trans
}

Total and partial variances

Recall the previous Local Sensitivity Assignment where you calculated the:

  • variances for each parameter based on its scale of variance and the local sensitivity of that parameter w.r.t. MRESULT()
  • calculate the sum of these variances
  • calculate the relative contribution of each parameter to the total variance
  • plot a pie chart to visualize the contributions to the derived uncertainty
  • a table of fractions, knitr::kable() is already implemented to inspect the procentual sensitivities

Determine the derived uncertainty based on the aforementioned list

#a table for some results "relvarM" contains the relative variances of MRESULT
knitr::kable(as.data.frame(relvarM)*100, caption = "percentage local sensitivities",align="c")

1.0095225

LS0tDQp0aXRsZTogIkhvb2dlIFJhYW0gb3BlbiB3YXRlciB3aXRoIGdyb3VuZHdhdGVyIGludGVyYWN0aW9ucyINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KICAgIGNzczogd2FudF9uLmNzcw0KICAgIGRmX3ByaW50OiBwYWdlZA0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogeWVzDQogICAgY3NzOiB3YW50X24uY3NzDQogIHBkZl9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KLS0tDQoNCiMgSW50cm9kdWN0aW9uIGNoYW5nZQ0KDQoNCldpdGggdGhpcyBmaW5hbCBhc3NpZ25tZW50IGJvdGggYW4gb3BlbiB3YXRlciBtb2RlbCBhbmQgYSBncm91bmR3YXRlciBtb2RlbCB3aWxsIGJlIGRldmVsb3BlZCB3aGljaCB3aWxsIGJlIGNvdXBsZWQuICANCkRlIG9wZW4gd2F0ZXIgbW9kZWwgcmVwcmVzZW50cyBhIHNtYWxsIHJpdmVyIGNhbGxlZCB0aGUgSG9vZ2UgUmFhbS4gVGhlIGdyb3VuZHdhdGVyIG1vZGVsIHdpbGwgc2ltdWxhdGUgdGhlIHRyYW5zaXRpb24gb2YgcHJlY2lwaXRhdGlvbiBpbnRvIGRyYWluYWdlIGFuZCBzdXJmYWNlIHJ1bm9mZiBvZiB0aGUgYXJlYSBzdXJyb3VuZGluZyB0aGUgSG9vZ2UgUmFhbS4gIA0KRmxvdyBpbiB0aGUgb3BlbiB3YXRlciBtb2RlbCBpcyBiYXNlZCBvbiB0aGUgZGlmZnVzaXZlIHdhdmUgKCJGcm91ZGUgPSAwIikgYXBwcm94aW1hdGlvbiBpbiAxRC4gR3JvdW5kd2F0ZXIgZmxvdyBpcyBiYXNlZCBvbiBEYXJjeSdzIGVxdWF0aW9uIGFuZCBzaW11bGF0ZXMgdGhlIGZsb3cgYmV0d2VlbiB0d28gb3BlbiB3YXRlciBjb3Vyc2VzIChkaXRjaGVzIG9yIGRyYWlucykgaW4gMUQuICBHcm91bmR3YXRlciBmbG93IHdpbGwgYmUgc2ltdWxhdGVkIGluIHRyYW5zaWVudCBtb2RlIHRvIGFjY291bnQgZm9yIHN0b3JhZ2UgY2hhbmdlcy4gVGhlIG9wZW4gd2F0ZXIgZmxvdyBtb2RlbCBzaW11bGF0ZXMgaW4gcHNldWRvIHN0YXRpb25hcnkgbW9kZS4gIA0KQSBzdG9ybSBldmVudCwgYSBoZWF2eSByYWluIHNob3dlciwgaXMgdGhlIGZvcmNpbmcgZm9yIHdoaWNoIHdlIHdpbGwgaW52ZXN0aWdhdGUgZmxvdyBmcm9tIHRoZSBzdXJmYWNlIGludG8gdGhlIHNvaWwsIHRvd2FyZHMgZGl0Y2hlcyBhbmQgZmluYWxseSB0byB0aGUgSG9vZ2UgUmFhbSB0byBnZXQgZGlzY2hhcmdlZCBhdCB0aGUgd2VpciBvbiB0aGUgbG93ZXIgc2lkZS4NCg0KVGhlIEhvb2dlIFJhYW0gaXMgbG9jYXRlZCBqdXN0IGJlbG93IHRoZSBzbWFsbCB0b3duIEdyYXZlIGFuZCBpcyBwYXJ0IG9mIHRoZSB3YXRlciBib2FyZCAiQWEgZW4gTWFhcyIuIFRoZSBzdXJyb3VuZGluZyBhcmVhIGlzIG1haW5seSB1c2VkIGFzIGdyYXNzbGFuZHMsIHNvbWUgY29ybmZpZWxkcyBhbmQgYSBmZXcgZm9yZXN0IHBsb3RzLiBUaGUgYXJlYSBpcyByZWFzb25hYmx5IGZsYXQgd2hlcmUgc3VyZmFjZSBsZXZlbHMgcmFuZ2UgZnJvbSAxNS41IHRpbGwgYWJvdXQgMTcuNSBtIEFNU0wgKEFib3ZlIE1lYW4gU2VhIExldmVsKS4NCg0KVGhlIGZpZ3VyZSBiZWxvdyBnaXZlcyBhbiBvdmVydmlldyBvZiB0aGUgSG9vZ2UgUmFhbSB3aGljaCB3aWxsIGJlIGNvbnNpZGVyZWQgZm9yIG1vZGVsaW5nLg0KDQohW092ZXJ2aWV3IEhvb2dlIFJhYW0gYW5kIGFyZWFdKEhvb2dlLXJhYW1fb3ZlcnZpZXcucG5nKQ0KDQpUaGUgZmlyc3QgcGFydCBjb250YWlucyB0aGUgZXhwbGFuYXRpb24gYW5kIGFzc2lnbm1lbnRzIGZvciB0aGUgb3BlbiB3YXRlciBtb2RlbCwgdGhlIHNlY29uZCB3aWxsIGdvIGludG8gdGhlIGRldGFpbHMgb2YgdGhlIGdyb3VuZHdhdGVyIG1vZGVsLCB0aGUgY291cGxpbmcgb2YgYm90aCBtb2RlbHMgaXMgZGVzY3JpYmVkIGluIHRoZSB0aGlyZCBwYXJ0IGFuZCBmaW5hbGx5IHRoZSBsYXN0IHBhcnQgKDQpIGNvbnRhaW5zIHRoZSBzZXR1cCB0byBkZXRlcm1pbmUgZGVyaXZlZCB1bmNlcnRhaW50aWVzIG9mIHRoZSAibW9kZWwtdHJhaW4iLg0KDQojIFBBUlQgMSB0aGUgT3BlbiB3YXRlciBtb2RlbA0KDQojIyBPcGVuIHdhdGVyIGVxdWF0aW9ucw0KDQpUaGlzIGRvY3VtZW50IHNob3VsZCBnaXZlIGFuIGludHJvZHVjdGlvbiB0byB0aGUgaHlkcmF1bGljIG1vZGVsaW5nIG9mIHRoZSBIb29nZSBSYWFtIHJpdmVyLg0KDQpBbiBhdHRlbXB0IGlzIG1hZGUgdG8gbWFrZSB0aGUgZG9jdW1lbnQgc2VsZiBjb250YWluZWQuIEZvciB0aGF0IHJlYXNvbiB3ZSBzdGFydCBieSBnaXZpbmcgc29tZSBiYXNpYyBmb3JtdWxhcywgYW5kIG1vcmUgZm9ybXVsYXMgd2lsbCBmb2xsb3cgZnVydGhlciBpbiB0aGUgZG9jdW1lbnQuIFRoaXMgZG9jdW1lbnQgc2hvdWxkIGhvd2V2ZXIgbm90IHRvIGJlIGNvbnNpZGVyZWQgYXMgYSBjb3Vyc2UgaW4gaHlkcm9keW5hbWljcy4gVGhlIGZvcm11bGFzIGFyZSBjaXRlZCAod2hpY2ggc2hvdWxkIGJlIGZvciBtb3N0IG9mIHlvdSBhIHJlcGV0aXRpb24/KSB0byBmb3JtIHRoZSBiYXNlIG9mIHRoZSBjb2RlLg0KDQpUaGUgZmxvdyBpbiByaXZlcnMgYXMgdGhlIEhvb2dlIFJhYW0gaXMgbW9zdCBvZnRlbiBkZXNjcmliZWQgYnkgdGhlIHNvIGNhbGxlZCBTdC1WZW5hbnQgZXF1YXRpb25zLCBleHByZXNzaW5nIHJlc3BlY3RpdmVseSB0aGUgbWFzcyBhbmQgdGhlIG1vbWVudHVtIGJhbGFuY2UgKG9ubHkgaW4gKngqLWRpcmVjdGlvbjoNCg0KJCQNClxmcmFje1xwYXJ0aWFsIEF9e1xwYXJ0aWFsIHR9ICsgXGZyYWN7XHBhcnRpYWwgUX17XHBhcnRpYWwgeH0gPSBJXFwNClxmcmFje1xwYXJ0aWFsIFF9e1xwYXJ0aWFsIHR9ICsgXGZyYWN7XHBhcnRpYWwgUVwsdX17XHBhcnRpYWwgeH0gDQogICAgICAgICAgICA9IGdcO0FcO1xCaWcoIFNfbyAtIFNfZiAtIFxmcmFje1xwYXJ0aWFsIGF9e1xwYXJ0aWFsIHh9XEJpZykNCiQkDQoNCkFzIHdlIGFyZSBoZXJlIGxlc3MgaW50ZXJlc3RlZCBpbiBoaWdobHkgZHluYW1pYyBvcGVuIHdhdGVyIGNhbGN1bGF0aW9ucywgYSBzaW1wbGlmaWNhdGlvbiBvZiB0aGUgc2Vjb25kIGVxdWF0aW9uIGFib3ZlIHdpbGwgYmUgdXNlZC4gVGhpcyBzaW1wbGlmaWNhdGlvbiBzdGFydHMgZnJvbSB0aGUgb2JzZXJ2YXRpb24gdGhhdCBpbiBtYW55IHNpdHVhdGlvbnMgKGNlcnRhaW5seSBsb3dsYW5kIHNpdHVhdGlvbnMpIHRoZSBmaXJzdCB0d28gdGVybXMgaW4gdGhlIG1vbWVudHVtIHBhcnQgb2YgdGhlIFN0LVZlbmFudCBlcXVhdGlvbnMgYXJlIG11Y2ggc21hbGxlciB0aGFuIHRoZSB0ZXJtcyBvbiB0aGUgcmlnaHQgaGFuZCBzaWRlLg0KDQokJA0KXGZyYWN7XHBhcnRpYWwgUX17XHBhcnRpYWwgdH0gKyBcZnJhY3tccGFydGlhbCBRXCx1fXtccGFydGlhbCB4fSA9IA0KICAgICAgICBnXDtBXDtcQmlnKCBTX28gLSBTX2YgLSBcZnJhY3tccGFydGlhbCBhfXtccGFydGlhbCB4fVxCaWcpXFwNClxEb3duYXJyb3cgXFwNCjAgPSBnXDtBXDtcQmlnKCBTX28gLSBTX2YgLSBcZnJhY3tccGFydGlhbCBhfXtccGFydGlhbCB4fVxCaWcpXFwNClxEb3duYXJyb3cgXFwNCjAgPSAgU19vIC0gU19mIC0gXGZyYWN7XHBhcnRpYWwgYX17XHBhcnRpYWwgeH0NCiQkDQoNClRoZSBtZWFuaW5nIG9mIHRoZSB0ZXJtcyBpbiB0aGVzZSBlcXVhdGlvbnMgd2lsbCBiZSBleHBsYWluZWQgd2hlbiB0aGV5IGZpcnN0IGFwcGVhciBpbiB0aGlzIGRvY3VtZW50Lg0KDQpJbiB0aGVzZSBleGVyY2lzZXMgc2l0dWF0aW9ucyB3aWxsIGJlIHN0dWRpZWQgd2hlcmUNCg0KLSAgIHRoZSBmbG93IGluIHRoZSBIb29nZSBSYWFtIGNhbiBiZSBjb25zaWRlcmVkIHRvIGJlICpzdGF0aW9uYXJ5KiAoc28gdGhlIHRpbWUgZGVyaXZhdGl2ZXMgaW4gdGhlIGVxdWF0aW9ucyBhYm92ZSBkaXNhcHBlYXIpDQotICAgdGhlcmUgaXMgYSB3ZWlyIGF0IHRoZSBkb3duc3RyZWFtIGVuZCBvZiB0aGUgcml2ZXINCg0KU2l0dWF0aW9ucyBvZiB0aGlzIHR5cGUgYXJlIG9mdGVuIGNhbGxlZCAqKmJhY2t3YXRlciBjdXJ2ZXMqKi4NCg0KVGhpcyB0eXBlIG9mIG9wZW4gd2F0ZXIgbW9kZWwgdGhhdCB3aWxsIGJlIGRldmVsb3BlZCBpbiB0aGlzIGRvY3VtZW50IHdpbGwgYmUgY291cGxlZCB3aXRoIGEgZ3JvdW5kd2F0ZXIgbW9kZWwuIFNvIGF0IHNvbWUgcGxhY2VzIHR5cGljYWwgZ3JvdW5kd2F0ZXIgdGVybXMgYW5kIGRpbWVuc2lvbnMgbWF5IG9jY3VyLg0KDQpUaGUgb3BlbiB3YXRlciBoYXMgaXRzIG93biBzdGFuZGFyZCB1bml0czoNCg0KLSAgIGxlbmd0aCB1bml0ID0gJG0kIG1ldGVyDQotICAgdGltZSB1bml0ID0gJHMkIHNlY29uZA0KDQojIyBIb29nZSBSYWFtIGRpbWVuc2lvbnMgYW5kIHBhcmFtZXRlcnMNCg0KV2UgYXJlIGdvaW5nIHRvIHNpbXVsYXRlIHRoZSBvcGVuIHdhdGVyIGZsb3cgb2YgdGhlIEhvb2dlIFJhYW0gc3RhcnRpbmcgYXQgdGhlIHNvdXRoZXJuIHBhcnQgYXQgdGhlICJWbGllc3dlZyIgd2l0aCBhIHByZXNjcmliZWQgZmx1eCBjb21pbmcgZnJvbSB0aGUgZGlzY2hhcmdpbmcgdXBzdHJlYW0gYXJlYS4gQXQgdGhlIG5vcnRoZXJuIHBhcnQgb2YgdGhlIEhvb2dlIFJhYW0gc2VjdGlvbiBpdCBkaXNjaGFyZ2VzIHRocm91Z2ggYSB3ZWlyLCB3aGljaCBmaW5hbGx5IGlzIGdldHRpbmcgZGlzY2hhcmdlZCBpbnRvIHRoZSBNZXVzZS4NCg0KV2Ugd2lsbCB1c2UgdGhlIGZvbGxvd2luZyBkYXRhIGZvciBkaW1lbnNpb25pbmcgdGhlIHJpdmVyLiBEYXRhIGlzIGJhc2VkIG9uICJsZWdnZXIiIHdhdGVyYm9hcmQgQWEgZW4gTWFhcy4NCg0KWW91IG1heSB3YW50IHRvIHF1aWNrbHkgYnJvd3NlIHRocm91Z2ggdGhpcyAibGVnZ2VyIiB0byBzZWUgd2hhdCBpbmZvcm1hdGlvbiBpcyBhdmFpbGFibGUuIEdvIHRvIGh0dHBzOi8vd3d3LmFhZW5tYWFzLm5sL29uc3dlcmsvcmVnZWxzL2xlZ2dlci8gYW5kIG9wZW4gdGhlICdMZWdnZXIgb3BwZXJ2bGFrdGV3YXRlcicuDQoNCg0KVGhlIGxlbmd0aCBvZiB0aGlzIHJlYWNoIG9mIHRoZSBIb29nZSBSYWFtIGlzIDE0NzAgbS4gVXBzdHJlYW0gYm90dG9tIGxldmVsIDE0LjUwIG0gQU1TTCwgZG93bnN0cmVhbSBib3R0b20gbGV2ZWwgMTEuODAgbSBBTVNMLCAoYXZlcmFnZSkgYm90dG9tIHdpZHRoIDIuMTAgbSwgKGF2ZXJhZ2UpIHNpZGUgc2xvcGUgMS41IG0vbS4NCg0KV2VpcjogY3Jlc3Qgd2lkdGggMi4wIG0sIGNyZXN0IGhlaWdodDogMTEuNDUgYW5kIDEyLjYwIG0uDQoNCkluZmx1eCBzdGF0aW9uYXJ5IHVwc3RyZWFtIGJhc2VkIG9uIGJhc2UgZmxvdyBhcyBkaXNjaGFyZ2Ugb2YgYW4gdXBzdHJlYW0gYXJlYSBvZiAyNTAgaGEgKGFzc3VtZSBhIGRpc2NoYXJnZSBvZiAwLjUgbC9zL2hhKS4NCg0KYGBge3J9DQpybShsaXN0ID0gbHMoKSkNCkhSLlFpbiA9IDI1MCowLjAwNSAgIzI1MGhhICogMC41IGwvcy9oYQ0KY2F0KCdUb3RhbCBpbmZsdXggdXBzdHJlYW0gOiAnLEhSLlFpbikNCmBgYA0KDQojIyBMb25naXR1ZGluYWwgdmlldw0KDQpJdCBpcyBjb252ZW50aW9uYWwgdG8gbWVhc3VyZSB0aGUgbGVuZ3RoIGFsb25nIHRoZSByaXZlciBmcm9tIHVwc3RyZWFtIHRvIGRvd25zdHJlYW0uIFRoZSBkb21haW4gb2YgdGhlIHJpdmVyIHN0YXJ0cyBhdCAwIG0uDQoNCldlIHdpbGwgYWxzbyBuZWVkIHRoZSBib3R0b20gc2xvcGUgaW4gd2hhdCBmb2xsb3dzLiBJbiB0aGUgZXF1YXRpb25zIG9mIHRoZSBpbnRyb2R1Y3Rpb24gdGhpcyBzbG9wZSB3YXMgY2FsbGVkICRTX28kLiBJdCBpcyBkZWZpbmVkIGFzIHRoZSBkcm9wIG9mIGJvdHRvbSBsZXZlbCBwZXIgdW5pdCBsZW5ndGggb2YgdGhlIHJpdmVyLiBIZXJlIGl0IGlzIGp1c3QgYSBjb25zdGFudDoNCg0KYGBge3IsIGV2YWw9VFJVRX0NCkhSLmxlbmd0aCA9IDE0NzANCkhSLmRvbWFpbiA9IGMoMCxIUi5sZW5ndGgpDQpIUi5ib3RlbGV2LnVwc3RyZWFtID0gMTQuNTANCkhSLmJvdGVsZXYuZG93bnN0cmVhbSA9IDExLjgwDQpIUi5TbyA9IChIUi5ib3RlbGV2LnVwc3RyZWFtIC1IUi5ib3RlbGV2LmRvd25zdHJlYW0pLyhIUi5sZW5ndGgpICMlIGJvdGVsZXYudXBzdHJlYW0gIyUgYm90ZWxldi5kbnN0cmVhbSAjJSBsZW5ndGgNCmNhdCgnU28gPSAnLEhSLlNvKQ0KDQpIUi56YiA9IGFwcHJveGZ1bihIUi5kb21haW4sYyhIUi5ib3RlbGV2LnVwc3RyZWFtLEhSLmJvdGVsZXYuZG93bnN0cmVhbSksDQogICAgICAgICAgICAgICAgICBydWxlPTIpDQpwbG90KEhSLmRvbWFpbixIUi56YihIUi5kb21haW4pLHR5cGUgPSAibCIsY29sPSJicm93biIsbHdkPTQsDQogICAgIG1haW49Ikhvb2dlIFJhYW0gYmVkIGJvdHRvbSIseGxhYj0ieCAobSkiLCB5bGFiPSAieiAobSkiKQ0KZ3JpZCgpDQpgYGANCg0KDQpUaGlzIGJvdHRvbSBzbG9wZSBicmluZ3MgdGhlIGdyYXZpdHkgaW50byB0aGUgZXF1YXRpb25zOiB0aGUgaGlnaGVyIHRoaXMgc2xvcGUsIHRoZSBsYXJnZXIgdGhlIGluZmx1ZW5jZSBvZiB0aGUgZ3Jhdml0eSBvbiB0aGUgd2F0ZXIgZmxvdy4NCg0KIyMgQ3Jvc3Mgc2VjdGlvbiBnZW9tZXRyeQ0KDQpUaGUgY3Jvc3Mgc2VjdGlvbiBjYW4gaW4gZ2VuZXJhbCBoYXZlIGEgY29tcGxleCBhbmQgc3BhdGlhbGx5IHZhcnlpbmcgZm9ybS4gSGVyIHdlIHRha2UganVzdCBhIHNpbXBsZSB0cmFwZXpvaWRhbCBwcm9maWxlIHdpdGggYSBjb25zdGFudCBib3R0b20gd2lkdGggJGIkIGFuZCBjb25zdGFudCBzaWRlIHNsb3BlICRtJDoNCg0KYGBge3IsIGV2YWw9VFJVRX0NCg0KSFIuYj0yLjEgICAjIGJvdHRvbSAgd2lkdGgNCkhSLm09MS41ICAgIyBzaWRlIHNsb3BlDQpgYGANCg0KVGhlIHdhdGVyIGxldmVsIHdpdGggcmVzcGVjdCB0byB0aGUgYm90dG9tIG9mIHRoaXMgY3Jvc3Mgc2VjdGlvbiBpcyBjYWxsZWQgdGhlICp3YXRlciBkZXB0aCogYW5kIHdpbGwgYmUgZGVub3RlZCBieSAkYSQuIFRvIG1ha2UgYSBwbG90IG9mIHRoZSBjcm9zcyBzZWN0aW9uIHdlIG5lZWQgdG8gY2FsY3VsYXRlIHRoZSB3aWR0aCBmb3IgYSB3aG9sZSByYW5nZSBvZiBwb3NzaWJsZSB3YXRlciBkZXB0aHMuDQoNCkZvciBhIHRyYXBlem9pZGFsIGNyb3NzIHNlY3Rpb24gdGhpcyByZXN1bHRzIGluOg0KDQpgYGB7ciwgZXZhbD1UUlVFfQ0KDQpIUi53aWR0aCA9IGZ1bmN0aW9uKGEpDQp7DQogIHJldHVybihIUi5iKzIqSFIubSphKQ0KfQ0KYGBgDQoNClRoZSBuZXh0IGNodW5rIGNyZWF0ZXMgYSBwbG90IG9mIHRoZSBjcm9zcyBzZWN0aW9uDQoNCmBgYHtyLCBldmFsPVRSVUV9DQoNCmFyYW5nZT1zZXEoMCwxLjUsbGVuZ3RoPTEwMCkgIyBsYXJnZSBlbm91Z2ggb2YgcG9zc2libGUgZGVwdGggdmFsdWVzIA0Kd2lkdGhyYW5nZSA9IEhSLndpZHRoKGFyYW5nZSkNCnBsb3QoYygtcmV2KHdpZHRocmFuZ2UvMiksd2lkdGhyYW5nZS8yKSxjKHJldihhcmFuZ2UpLGFyYW5nZSksDQogICAgIHhsYWI9InkiLHlsYWI9ImEiLG1haW49ImNyb3NzIHNlY3Rpb24iLHR5cGU9ImwiLGx3ZD01LGNvbD0iYnJvd24iKQ0KZ3JpZChjb2w9ImJsYWNrIikNCmBgYA0KDQojIyBXZXR0ZWQgYXJlYQ0KDQpPbmUgb2YgdGhlIGltcG9ydGFudCBjcm9zcyBzZWN0aW9uIGZ1bmN0aW9ucyBpcyB0aGUgd2V0dGVkIGFyZWEuIEl0IHdhcyBpbiB0aGUgU3QtVmVuYW50IGVxdWF0aW9uIGRlbm90ZWQgYnkgJEEkLiBGb3IgYSB0cmFwZXpvaWRhbCBjcm9zcyBzZWN0aW9uIHRoaXMgY2FuIGJlIGVhc2lseSBjYWxjdWxhdGVkIGJ5IHRoZSBmb2xsb3dpbmcgZnVuY3Rpb246DQoNCmBgYHtyLCBldmFsPVRSVUV9DQoNCkhSLkEgPSBmdW5jdGlvbihhKQ0Kew0KICByZXR1cm4oSFIuYiphK0hSLm0qYV4yKQ0KfQ0KYGBgDQoNCk9uZSBvZiB0aGUgcmVhc29ucyB0aGF0IHRoaXMgd2V0dGVkIGNyb3NzIHNlY3Rpb24gaXMgc28gaW1wb3J0YW50IGlzIGJlY2F1c2Ugb2YgdGhlIGltcG9ydGFudCBmb3JtdWxhICRRID0gdiBBJCwgd2VyZSAkUSQgaXMgdGhlIGRpc2NoYXJnZSwgJHYkIGlzIHRoZSB2ZWxvY2l0eSBhbmQgJEEkIGlzIHRoZSB3ZXR0ZWQgY3Jvc3Mgc2VjdGlvbi4gDQpTb21lIHR5cGljYWwgKG1heCkgdmVsb2NpdGllcyBkZXBlbmRpbmcgb2YgdGhlIHNvaWwgdHlwZSBhcmUgZ2l2ZW4gdGhlIHRhYmxlIGJlbG93ICANCg0KfCBTb2lsIHR5cGUgICAgICAgICAgIHwgdmVsb2NpdHkgbS9zICAgfA0KfDotLS0tLS0tLS0tLS0tLS0tLS0tOnw6LS0tLS0tLS0tLS0tLS06fA0KfCBjbGF5L2xvYW0vbG9lc3MgICAgIHwgMC42MCAtIDAuODAgICAgfA0KfCBzYW5kL3NvbGlkIHBlYXQgICAgIHwgMC4zMCAtIDAuNjAgICAgfA0KfCBjb2Fyc2Ugc2FuZCAgICAgICAgIHwgMC4yMCAtIDAuNTAgICAgfA0KfCBmaW5lIHNhbmQvc29mdCBwZWF0IHwgMC4xNSAtIDAuMzAgICAgfA0KDQo6IHNvdXJjZTogQ3VsdHV1cnRlY2huaXNjaCBWYWRlbWVjdW0gMTk4OA0KDQpBcyBjYW4gYmUgc2VlbiBpbiB0aGUgZnVuY3Rpb24gYWJvdmUgdGhhdCB0aGUgd2V0dGVkIGNyb3NzIHNlY3Rpb24gaXMgZGVwZW5kZW50IG9uIHRoZSB3YXRlciBkZXB0aCAkYSQuIE9uZSB3YXkgdG8gc2VlIHRoZSBpbmZsdWVuY2Ugb2YgdGhlIHdldHRlZCBjcm9zcyBzZWN0aW9uIG9uIHRoZSBkaXNjaGFyZ2UgaXMgdG8gaW52ZXN0aWdhdGUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuICRRJCBhbmQgJGEkIGZvciBmaXhlZCB2ZWxvY2l0aWVzLiBIZXJlIGlzIGEgdHlwaWNhbCBleGFtcGxlIChpdCBpcyBjdXN0b21hcnkgdG8gcGxvdCB3YXRlciBkZXB0aHMgb24gdGhlIHZlcnRpY2FsIGF4aXMpOg0KDQpgYGB7ciwgZXZhbD1UUlVFfQ0KQXJhbmdlID0gIEhSLkEoYXJhbmdlKQ0KdmVsID0gMC41MCAjbS9zIHZlbG9jaXR5IGZvciBjb2Fyc2Ugc2FuZA0KUXJhbmdlID0gdmVsKkFyYW5nZQ0KcGxvdChRcmFuZ2UsYXJhbmdlLA0KICAgICBtYWluPSJIb29nZSBSYWFtIFEtYSByZWxhdGlvbiBmb3IgZml4ZWQgdmVsb2NpdHkgdiIsDQogICAgIHhsYWI9IlEgKG1eMy9zKSIseWxhYj0iYSAobSkiLGNvbD0iYnJvd24iLGx3ZD0zLHR5cGU9ImwiKQ0KbGVnZW5kKCJib3R0b21yaWdodCIsIGluc2V0PS4wNSwgdGl0bGU9InYgKG0vcykiLA0KICAgICAgIGMoIjAuNSIpLCBjb2w9YygiYnJvd24iKSxsd2Q9Myxob3Jpej1UUlVFKQ0KZ3JpZChjb2w9ImJsYWNrIikNCmBgYA0KDQpWZWxvY2l0aWVzIGhpZ2hlciB0aGFuIDEgbS9zIGFyZSBob3dldmVyIHJhdGhlciBhdHlwaWNhbC4NCg0KDQo8ZGl2IGNsYXNzPSJxdWVzdGlvbiI+DQpGaWxsIHRoZSBjaHVuayBiZWxvdyAocmVwbGFjZSBhbGwgWFhYKSB3aXRoIGEgY29kZSB0aGF0IGNvbWJpbmVzIGluIG9uZVwNCnBsb3QgdGhlIGEtUSBmb3IgZm91ciByZWFsaXN0aWMgdmVsb2NpdGllcy4NCg0KYGBge3IsIGV2YWw9VFJVRX0NCkFyYW5nZSA9ICBIUi5BKGFyYW5nZSkgI0NhbGN1bGF0aW5nIHdldHRlZCBhcmVhDQpRMXJhbmdlID0gMC4xNSpBcmFuZ2UgICNDbGF5L2xvYW0vbG9lc3MgdmVsb2NpdHkNClEycmFuZ2U9ICAwLjIwKkFyYW5nZSAgICNTYW5kL3NvbGlkIHBlYXQgdmVsb2NpdHkNClEzcmFuZ2U9ICAwLjMwKkFyYW5nZSAgI0NvYXJzZSBzYW5kIA0KUTRyYW5nZSA9IDAuNjAqQXJhbmdlICAgICAjRmluZSBzYW5kL3NvZnQgcGVhdA0KcGxvdChRNHJhbmdlLGFyYW5nZSwgICAjTWFrZSBhIHBsb3Qgb2YgZmluZSBzYW5kDQogICAgIG1haW49Ikhvb2dlIFJhYW0gUS1hIHJlbGF0aW9uIGZvciBmaXhlZCB2ZWxvY2l0eSB2IiwgI1RpdGxlDQogICAgIHhsYWI9ZXhwcmVzc2lvbihRfihtXjMvcykpLHlsYWI9ImEgKG0pIixjb2w9ImJyb3duIixsd2Q9Myx0eXBlPSJsIiwgeWxpbT1jKDAsMS41KSwgeGxpbSA9IGMoMCwyKSkgI1NldHRpbmdzIGZvciBwbG90DQpsaW5lcyhRM3JhbmdlLGFyYW5nZSxsd2Q9Myxjb2w9ImJsdWUiKSAjQWRkaW5nIHBsb3Qgb2YgY29hcnNlIHNhbmQNCmxpbmVzKFEycmFuZ2UsYXJhbmdlLGx3ZD0zLGNvbD0icmVkIikgICNBZGRpbmcgcGxvdCBvZiBzYW5kL3NvbGlkIHBlYXQNCmxpbmVzKFExcmFuZ2UsYXJhbmdlLGx3ZD0zLGNvbD0iZ3JlZW4iKSAjQWRkaW5nIHBsb3Qgb2YgY2xheS9sb2FtL2xvZXNzDQoNCmxlZ2VuZCgiYm90dG9tcmlnaHQiLCBpbnNldD1jKDAuMDAxKSwgdGl0bGU9InYgKG0vcykiLCAjQWRkaW5nIHRoZSBsZWdlbmQNCmMoImNsYXksIDAuMTUiLCJzYW5kLCAwLjIwIiwiY29hcnNlIHNhbmQsIDAuMzAiLCJmaW5lIHNhbmQsIDAuNjAiKSwgY29sPWMoImdyZWVuIiwicmVkIiwiYmx1ZSIsImJyb3duIiksbHdkPTMsaG9yaXo9VFJVRSwgY2V4ID0gMC42KSAjQ29sb3IgdGhlIGRpZmZlcmVuY2Ugc2NlbmFyaW8NCg0KZ3JpZChjb2w9ImJsYWNrIikgI01ha2luZyB0aGUgY29sb3Igb2YgdGhlIGdyaWQgdG8gYmUgYmxhY2sNCg0KDQpgYGANCjwvZGl2Pg0KDQoNCg0KIyMgRXF1aWxpYnJpdW0gZGlzY2hhcmdlDQoNCkJ1dCBvZiBjb3Vyc2UgdXN1YWxseSBib3RoIGFuZCAkQSQgYW5kICR2JCBjaGFuZ2UgaWYgdGhlIHdhdGVyIGRlcHRoICRhJCBjaGFuZ2VzLg0KDQpGb3IgZXF1aWxpYnJpdW0gc2l0dWF0aW9ucyB0aGlzIHJlbGF0aW9uc2hpcCBjYW4gYmUgaW52ZXN0aWdhdGVkIHRocm91Z2ggZm9ybXVsYXMuIEVxdWlsaWJyaXVtIG1lYW5zIHRoYXQgdGhlIHdhdGVyIGRlcHRoIGlzIGNvbnN0YW50IGluIHNwYWNlICgkXGZyYWN7XHBhcnRpYWwgYX17XHBhcnRpYWwgeH0gPSAwJCkgaW4gdGVybXMgb2YgdGhlIFN0LVZlbmFudCBlcXVhdGlvbnMgb2YgdGhlIGludHJvZHVjdGlvbikgYW5kIHRpbWUuIEZvciB0aGlzIHNpdHVhdGlvbiB0aGUgb3BlbiB3YXRlciBlcXVhdGlvbiBzaW1wbGlmaWVzIGludG87ICQkU18wID0gU19mID0gXGZyYWN7bl4yXDsgUV4yfXtBXjJcOyBSXns0LzN9fSQkICAgDQoNCkZyb20gdGhpcyBvbmUgY2FuIGRlcml2ZTogJCRRX1xtYXRocm17ZXF1aX0gPSBcZnJhY3tcc3FydHtTX299fXtufSBBXDsgUl57Mi8zfSQkIHdoZXJlDQoNCi0gICAkU19vJDogYm90dG9tIHNsb3BlIChhcyBjYWxjdWxhdGVkIGFib3ZlKQ0KDQotICAgJG4kOiBNYW5uaW5nIGNvZWZmaWNpZW50OiB0aGUgaGlnaGVyIHRoaXMgbnVtYmVyLCB0aGUgbW9yZSBmcmljdGlvbiwgdHlwaWNhbCB2YWx1ZXMgYXJlIGFyb3VuZCAwLjA0LCBidXQgZGlmZmljdWx0IHRvIGFzc2Vzcw0KDQotICAgJEEkOiB0aGUgd2V0dGVkIGNyb3NzIHNlY3Rpb24sIGFzIGRpc2N1c3NlZCBhbmQgY2FsY3VsYXRlZCBhYm92ZQ0KDQotICAgJFIkOiBoeWRyYXVsaWMgcmFkaXVzLiBGb3JtYWxseSBkZWZpbmVkIGFzIHdldHRlZCBBcmVhIGRpdmlkZWQgYnkgdGhlIHdldHRlZCBwZXJpbWV0ZXIoJFI9XGZyYWMge0F9e1B9JCksIGFzIGluIHRoZSBuZXh0IGNodW5rOg0KDQoNCg0KYGBge3IsIGV2YWw9VFJVRX0NCkhSLm4gPSAwLjA0NSANCg0KSFIuUiA9IGZ1bmN0aW9uKGEpDQp7DQogIHJldHVybigoSFIuYiphK0hSLm0qYV4yKS8oSFIuYisyKnNxcnQoMStIUi5tXjIpKmEpKQ0KfQ0KSFIuUWVxdWkgPSBmdW5jdGlvbihhKQ0Kew0KICBSID0gSFIuUihhKQ0KICBBID0gSFIuQShhKQ0KICBRID0gc3FydChIUi5TbykvSFIubipBKlJeKDIvMykgIyUgPQ0KICByZXR1cm4oc3FydChIUi5TbykvSFIubipBKlJeKDIvMykpDQp9DQoNClFlcXVpcmFuZ2UgPSBIUi5RZXF1aShhcmFuZ2UpDQpwbG90KFFlcXVpcmFuZ2UsYXJhbmdlLA0KICAgICBtYWluPSJIb29nZSBSYWFtIFEtYSBlcXVpbGlicml1bSIsDQogICAgIHhsYWI9IlFlcXVpIix5bGFiPSJhIixsd2Q9Myxjb2w9ImJsdWUiLHR5cGU9ImwiKQ0KZ3JpZChjb2w9ImJsYWNrIikNCmBgYA0KDQpTb21ldGltZXMgdGhlIHF1ZXN0aW9uIGlzIHBvc2VkIHRoZSBvdGhlciB3YXkgYXJvdW5kLiBBc3N1bWUgdGhhdCBvbmUgd2FudHMgdG8gZHJhaW4gYSBjb25zdGFudCBpbnRlbnNpdHkgb2YgMTAgJG1tL2RheSQgb2YgYW4gYXJlYSBvZiB0aGUgc2l6ZSBvZiAzMCAka21eMiQgdGhyb3VnaCB0aGlzIHJpdmVyLg0KDQo8ZGl2IGNsYXNzPSJxdWVzdGlvbiI+DQpTdGVwMTogY2FsY3VsYXRlIHRoZSBkaXNjaGFyZ2UgdGhyb3VnaCB0aGUgcml2ZXIuDQoNCmBgYHtyIGV2YWw9VFJVRX0NCmFyZWEgPSAzMCAjIGttXjINCmFyZWEgPSBhcmVhICooMTAwMCleMiAjIG1eMg0KSSA9IDEwICMgbW0gL2RheQ0KSSA9IEkvKDEwMDAqMjQqMzYwMCkgIyBtL3MgIysgPSANClEgPSBJKmFyZWENCmBgYA0KPC9kaXY+DQoNCg0KDQpOb3cgd2Ugd2FudCB0byBjYWxjdWxhdGUgdGhlIHdhdGVyIGxldmVsIGNvcnJlc3BvbmRpbmcgdG8gdGhlIGVxdWlsaWJyaXVtIGRpc2NoYXJnZSBqdXN0IGNhbGN1bGF0ZWQuIFdlIGNhbiB1c2UgdGhlIHBsb3QgbWFkZSBhYm92ZSwgb3Igd2UgY2FuIHVzZSB0aGUgZm9sbG93aW5nIGNodW5rIHRvIGZpbmQgdGhlIHdhdGVyIGxldmVsIGNvcnJlc3BvbmRpbmcgdG8gdGhlIGNhbGN1bGF0ZWQgZGlzY2hhcmdlIHJhdGUuDQoNCmBgYHtyLCBldmFsPVRSVUV9DQojaW5zdGFsbC5wYWNrYWdlcygibmxlcXNsdiIpDQpsaWJyYXJ5KG5sZXFzbHYpDQp0b3NvbHZlID0gZnVuY3Rpb24oYSl7cmV0dXJuIChIUi5RZXF1aShhKS1RKX0NCmFlcXVpID0gbmxlcXNsdigxLHRvc29sdmUpJHgNCnByaW50KGFlcXVpKQ0KYGBgDQo8ZGl2IGNsYXNzPSJxdWVzdGlvbiI+DQpSZXBlYXQgdGhlIGNhbGN1bGF0aW9uIGFib3ZlIGZvciB0aGUgcmFpbmZhbGwgaW50ZW5zaXRpZXMgYmV0d2VlbiAwLjUgbW0vZGF5IGFuZCAxNSBtbS9kYXkgYW5kIG1ha2UgYSBwbG90IG9mIHRoZSB3YXRlciBkZXB0aHMgdnMgdGhlc2UgaW50ZW5zaXRpZXMuDQoNCmBgYHtyIGV2YWw9VFJVRX0NCklzID0gc2VxKDAuNSwxNSxsZW5ndGg9MTAwKQ0KYWVxdWlzID0gYygpDQpmb3IoIGkgaW4gMTpsZW5ndGgoSXMpKQ0Kew0KICBJID0gSXNbaV0vKDM2MDAqMjQqMTAwMCkNCiAgUSA9IEkqYXJlYSAgICAgICAgICAgICAgICANCiAgdG9zb2x2ZSA9IGZ1bmN0aW9uKGEpe3JldHVybiAoSFIuUWVxdWkoYSktUSl9DQogIGFlcXVpc1tpXSA9IG5sZXFzbHYoMSx0b3NvbHZlKSR4DQp9DQpwbG90KElzLGFlcXVpcyx0eXBlPSJsIixjb2w9ImJsdWUiLGx3ZD0zLCBtYWluPSJXYXRlciBEZXB0aCBmb3IgRGlmZmVyZW50IFJhaW4gSW50ZW5zaXRpZXMiLCB4bGFiID0gIlJhaW5mYWxsIGludGVuc2l0eSAobW0vZGF5KSIsIHlsYWIgPSAiU3RlYWR5IHN0YXRlIHdhdGVyIGxldmVsIChtKSIpDQpncmlkKGNvbD0iYmxhY2siKQ0KYGBgDQo8L2Rpdj4NCg0KDQoNCiMgVGhlIGRvd25zdHJlYW0gd2Vpcg0KDQojIyBHZW9tZXRyeSBvZiB0aGUgd2Vpcg0KDQpTZWUgdGhlIGZvbGxvd2luZyBncmFwaA0KDQpgYGB7ciwgZXZhbD1UUlVFfQ0KSFIud2Vpci53aWR0aCA9IDIuMjUgICNtIHdpZHRoIG9mIHRoZSB3ZWlyIGZsb29yIA0KSFIud2Vpci5jcmVzdCA9IDEgI20gYWJvdmUgYm90dG9tIHJpdmVyDQojIGRyYXcgd2VpciBpbiBjcm9zcyBzZWN0aW9uDQpwbG90KGMoLXJldih3aWR0aHJhbmdlLzIpLHdpZHRocmFuZ2UvMiksYyhyZXYoYXJhbmdlKSxhcmFuZ2UpLA0KICAgICB4bGFiPSJ5IChtKSIseWxhYj0iYSAobSkiLG1haW49ImNyb3NzIHNlY3Rpb24iLHR5cGU9ImwiLGx3ZD01LGNvbD0iYnJvd24iKQ0KbGluZXMoYygtSFIud2Vpci53aWR0aC8yLC1IUi53ZWlyLndpZHRoLzIsSFIud2Vpci53aWR0aC8yLEhSLndlaXIud2lkdGgvMiksDQogICAgICBjKG1heChhcmFuZ2UpLEhSLndlaXIuY3Jlc3QsSFIud2Vpci5jcmVzdCxtYXgoYXJhbmdlKSkNCiAgICAgICxjb2w9ImJsdWUiLGx3ZD0zKQ0KbGVnZW5kKCJib3R0b21yaWdodCIsIGluc2V0PS4wNSwNCiAgICAgICBjKCJyaXZlciIsIndlaXIiKSwgY29sPWMoImJyb3duIiwiYmx1ZSIpLGx3ZD0zLGhvcml6PUZBTFNFKQ0KZ3JpZChjb2w9ImJsYWNrIikNCg0KYGBgDQoNCiMjIFdlaXIgUS1hIHJlbGF0aW9uDQoNClRoZSBhbW91bnQgb2Ygd2F0ZXIgZmxvd2luZyBvdmVyIGEgd2VpciBpcyBhIGZ1bmN0aW9uIG9mIHRoZSB3YXRlciBoZWlnaHQgYWJvdmUgdGhlIGNyZXN0LiBGb3IgYSByZWN0YW5ndWxhciB3ZWlyIHRoaXMgaXMgYSB3ZWxsIGtub3duIGZvcm11bGE6ICQkUV9cbWF0aHJte3dlaXJ9ID0gMS44M1w7IEJfXG1hdGhybXt3ZWlyfVw7IChhLWNfXG1hdGhybXt3ZWlyfSleezMvMn0kJA0KDQo8ZGl2IGNsYXNzPSJxdWVzdGlvbiI+DQpGaW5pc2ggdGhlIGNvZGUgYmVsb3cgc3VjaCB0aGF0IHRoZSBmdW5jdGlvbiBjYWxjdWxhdGVzIGRpc2NoYXJnZXMgYWNjb3JkaW5nIHRvIHRoZSBmb3JtdWxhIGFib3ZlLg0KDQpgYGB7ciwgZXZhbD1UUlVFfQ0KSFIud2VpclEgPSBmdW5jdGlvbihhKQ0Kew0KICBhW2E8SFIud2Vpci5jcmVzdF0gPSBIUi53ZWlyLmNyZXN0DQogIHJldHVybigxLjgzKkhSLndlaXIud2lkdGgqKGEtSFIud2Vpci5jcmVzdCleKDMvMikpDQp9DQoNCndlaXJRcmFuZ2UgPSBIUi53ZWlyUShhcmFuZ2UpDQoNCiMjIE1BS0UgVEhFIFBMT1QgTklDRVIgQlkgQURKVVNUSU5HIFhMSU0gWUxJTSBBTkQgTEVHRU5EDQoNCnBsb3QoUWVxdWlyYW5nZSxhcmFuZ2UsbWFpbj0iUWVxdWkgKHdpdGhvdXQgd2VpcikgYW5kIFF3ZWlyIGZvciBIb29nZVJhYW0iLA0KICAgICB4bGFiPSJRZXF1aSIseWxhYj0iYSIsbHdkPTMsY29sPSJibHVlIix0eXBlPSJsIiwgeWxpbSA9IGMoMCwxLjUpKQ0KbGluZXMod2VpclFyYW5nZSxhcmFuZ2UsbHdkPTMsY29sPSJyZWQiKQ0KbGVnZW5kKCJib3R0b21yaWdodCIsIGluc2V0PS4wNSwNCiAgICAgICBjKCJRZXF1aXJpdmVyIiwiUXdlaXIiKSwgY29sPWMoImJsdWUiLCJyZWQiKSxsd2Q9Myxob3Jpej1GQUxTRSkNCmdyaWQoY29sPSJibGFjayIpDQpgYGANCjwvZGl2Pg0KDQoNCg0KIyBSb3VnaCBiYWNrd2F0ZXIgYXBwcm94aW1hdGlvbg0KDQpGb3IgYW55IGdpdmVuIGZpeGVkIGRpc2NoYXJnZSAkUSQgdGhhdCBnb2VzIHRocm91Z2ggdGhlIEhvb2dlIFJhYW0sIHdlIGNhbiBjYWxjdWxhdGUgdHdvIHdhdGVyIGxldmVscyB3aXRoIHRoZSBpbmZvcm1hdGlvbiBhYm92ZToNCg0KLSAgIGEgd2F0ZXIgZGVwdGggYW5kIGxldmVsIGF0IHRoZSB3ZWlyDQoNCi0gICBhbiBlcXVpbGlicml1bSB3YXRlciBkZXB0aCBhbmQgbGV2ZWwgZmFyIHVwc3RyZWFtIG9mIHRoZSB3ZWlyDQoNCkluIGJldHdlZW4gdGhlIHdhdGVyIGxldmVscyB3aWxsIHZhcnkgYmV0d2VlbiB0aGVzZSB0d28uIEhlcmUgd2UgZmlyc3QgYXJlIGdvaW5nIHRvIGNhbGN1bGF0ZSB0aGVzZSBsZXZlbHMgYW5kIHRoZW4ganVzdCBhcyBhIGZpcnN0IHJvdWdoIGFwcHJveGltYXRpb24gaW50ZXJwb2xhdGUgbGluZWFyIGluIGJldHdlZW4uDQoNCjxkaXYgY2xhc3M9InF1ZXN0aW9uIj4NCkNhbGN1bGF0ZSBmb3IgUT0xLjIgdGhlIHdhdGVyIGRlcHRoIGF0IHRoZSB3ZWlyIGFuZCB0aGUgd2F0ZXIgZGVwdGggYXQgdGhlIHVwc3RyZWFtIGVuZA0KDQpgYGB7ciwgZXZhbD1UUlVFfQ0KbGlicmFyeShubGVxc2x2KQ0KUSA9IDEuMg0KI1dhdGVyIGxldmVsIGZhciB1cHN0cmVhbQ0KdG9zb2x2ZSA9IGZ1bmN0aW9uKGEpe3JldHVybiAoSFIuUWVxdWkoYSktUSl9DQphZXF1aSA9IG5sZXFzbHYoMSx0b3NvbHZlKSR4DQpwcmludChhZXF1aSkNCg0KI1dhdGVyIGxldmVsIGF0IHRoZSB3ZWlyDQp0b3NvbHZlID0gZnVuY3Rpb24oYSl7cmV0dXJuIChIUi53ZWlyUShhKS1RKX0NCmF3ZWlyID0gbmxlcXNsdigxLjEsdG9zb2x2ZSkkeA0KcHJpbnQoYXdlaXIpDQpgYGANCjwvZGl2Pg0KICANCg0KPGRpdiBjbGFzcz0icXVlc3Rpb24iPg0KTWFrZSBhIGZ1bmN0aW9uIGZvciB0aGUgcm91Z2ggbGluZWFyIGFwcHJveGltYXRpb24gYW5kIHBsb3QgaXQNCg0KYGBge3IsIGV2YWw9VFJVRX0NCkhSLmJ3cm91Z2ggPSBhcHByb3hmdW4oSFIuZG9tYWluLA0KICAgICAgICAgICAgICAgICAgICAgICBjKEhSLmJvdGVsZXYudXBzdHJlYW0rYWVxdWksSFIuYm90ZWxldi5kb3duc3RyZWFtK2F3ZWlyKSxydWxlPTIpICMlIGFlcXVpICMlIGF3ZWlyDQpwbG90KEhSLmRvbWFpbixIUi5id3JvdWdoKEhSLmRvbWFpbikseWxpbT1jKEhSLmJvdGVsZXYuZG93bnN0cmVhbSxIUi5ib3RlbGV2LnVwc3RyZWFtK2FlcXVpKSwNCiAgICAgbWFpbj1wYXN0ZSgicm91Z2ggbGV2ZWwgYXBwcm94IGZvciBRPSIsUSksDQogICAgIHhsYWI9ImwgKG0pIix5bGFiPSJoIChtKSIsY29sPSJibHVlIixsd2Q9NCx0eXBlPSJsIikNCg0KbGluZXMoSFIuZG9tYWluLEhSLnpiKEhSLmRvbWFpbiksY29sPSJicm93biIsbHdkPTQpDQoNCiNBZGQgbGVnZW5kLCBibHVlIGlzIHdhdGVyIGVsZXZhdGlvbiBhbmQgYnJvd24gaXMgcml2ZXIgYmVkIGVsZXZhdGlvbg0KYGBgDQo8L2Rpdj4gIA0KDQoNCg0KIyBTZXR1cCBvZiBiYXNlIGJhY2t3YXRlciBtb2RlbA0KDQpUaGUgcHJldmlvdXMgYmFja3dhdGVyIGFwcHJveGltYXRpb24gaXMgdG9vIHJvdWdoLiBJbiByZWFsaXR5IHRoZSB3YXRlciBsZXZlbHMgd2lsbCB2YXJ5IG5vbi1saW5lYXIgKGJ1dCBzdGlsbCB2ZXJ5IHNtb290aGx5KS4gQSAxRCBmbG93IG1vZGVsIGhhcyB0byBiZSBzZXR1cCB0byBjYWxjdWxhdGUgdGhlc2UgbGV2ZWxzLiBTbyB0aGUgMUQgZmxvdyBsaWJyYXJ5IG5lZWRzIHRvIGJlIGxvYWRlZDoNCg0KYGBge3IsIGV2YWw9VFJVRX0NCmxpYnJhcnkoRlZGRTFEKQ0KYGBgDQoNCiMjIFRoZSBjaG9pY2Ugb2Ygc3RhdGUNCg0KRm9yIGFsbCBraW5kIG9mIGxvY2FsIGNhbGN1bGF0aW9ucyB0aGUgd2F0ZXIgZGVwdGggJGEkIGlzIHRoZSBtb3N0IGNvbW1vbiBjaG9pY2UuIEZvciBub24tbG9jYWwgcHJvYmxlbXMgYXMgdGhlIGNhbGN1bGF0aW9uIG9mIGEgYmFja3dhdGVyIGN1cnZlLCB0aGUgKndhdGVyIGxldmVsKiB3aXRoIHJlc3BlY3QgdG8gQU1TTCAodGhlICRoJCBpbiB0aGUgcGxvdCBhYm92ZSkgaXMgdGhlIG1vc3QgY29tbW9uIGNob2ljZS4gVGhlc2UgdHdvIHF1YW50aXRpZXMgYXJlIGNvbm5lY3RlZCB0aHJvdWdoIHRoZSByaXZlciwgYm90dG9tIGhlaWdodDpcDQokJA0KICBoID0gel9iKHgpK2EgDQokJA0KDQpGb3IgdGhlIEhvb2dlIFJhYW0sIGEgZnVuY3Rpb24gZm9yIHRoZSBib3R0b20gd2FzIGFscmVhZHkgZ2l2ZW46ICoqSFIuemIqKiAoc2VlIGFib3ZlKS4gDQoNCg0KIyMgVGhlIGVxdWF0aW9uDQo8ZGl2IGNsYXNzPSJxdWVzdGlvbiI+DQpGaW5pc2ggdGhlIGZ1bmN0aW9uIGRlZmluaXRpb24gYmVsb3c6ICANCg0KYGBge3IgZXZhbCA9VFJVRX0NCkhSLmEgPSBmdW5jdGlvbih4LGgpDQp7DQogIHJldHVybihoLUhSLnpiKHgpKSAjDQp9DQpgYGANCjwvZGl2PiAgDQoNCg0KDQoNCg0KDQojIyBUaGUgaW50ZXJuYWwgZmx1eCBmdW5jdGlvbg0KDQojIyMgVGhlIHRoZW9yeQ0KDQpUaGUgbW9zdCBpbXBvcnRhbnQgc3RlcCBpbiBkZWZpbmluZyBhIGZsb3cgbW9kZWwgaXMgdGhlIGRlZmluaXRpb24gb2YgdGhlIGludGVybmFsIGZsdXguIFRoZSBkZWZpbml0aW9uIGZvbGxvd3MgZnJvbSB0aGUgZXF1YXRpb24gYXMgZ2l2ZW4gYXQgdGhlIHN0YXJ0IG9mIHRoaXMgZG9jdW1lbnQ6DQoNCiQkDQowID0gIFNfbyAtIFNfZiAtIFxmcmFje1xwYXJ0aWFsIGF9e1xwYXJ0aWFsIHh9IA0KJCQNCg0KVGhlIGZvcm11bGEgZm9yIHRoZSBmcmljdGlvbiBzbG9wZSwgJFNfZiQsIHJlcXVpcmVzIG5vdyBhIGJpdCBtb3JlIHNvcGhpc3RpY2F0aW9uLg0KDQpUaGUgZmxvdyBjYW4gbm93IGJlIHRvIHRoZSByaWdodCAoJFE+MCQpIG9yIHRvIHRoZSBsZWZ0ICgkUTwwJCkuIEFsdGhvdWdoIGluIHRoZSBnaXZlbiBzaXR1YXRpb24gYW5kIHdpdGggdGhlIGN1cnJlbnQgY2hvaWNlIG9mIGRvbWFpbiwgdGhlIGZsb3cgc2hvdWxkIGJlIHRvIHRoZSByaWdodCBhbmQgdGh1cyBwb3NpdGl2ZSwgaXQgY2FuIG5vdCBiZSBleGNsdWRlZCB0aGF0IGR1cmluZyB0aGUgbnVtZXJpY2FsIGNhbGN1bGF0aW9ucyBpdCBtYXkgYmUgKGxvY2FsbHkpIGRpZmZlcmVudC4gVGhlIGZyaWN0aW9uIHNsb3BlIGlzIHRlY2huaWNhbGx5IGRlZmluZWQgYXMgdGhlIGFtb3VudCBvZiBlbmVyZ3kgbG9zcyBpZiBnb2luZyBpbiB0aGUgcG9zaXRpdmUgeC1kaXJlY3Rpb24uDQoNCi0gICBpZiB0aGUgZmxvdyBpcyBpbiB0aGUgcG9zaXRpdmUgeC1kaXJlY3Rpb24gKCRRPjAkKSwgdGhlaXIgc2hvdWxkIGJlIGxvc3MgaW4gdGhlIHBvc2l0aXZlIHgtZGlyZWN0aW9uLCBzbyB0aGUgZnJpY3Rpb24gc2xvcGUgc2hvdWxkIGJlIHBvc2l0aXZlDQotICAgaWYgdGhlIGZsb3cgaXMgaW4gdGhlIG5lZ2F0aXZlIHgtZGlyZWN0aW9uICgkUTwwJCksIHRoZWlyIHNob3VsZCBiZSBsb3NzIGluIHRoZSBuZWdhdGl2ZSB4LWRpcmVjdGlvbiwgc28gdGhlIGZyaWN0aW9uIHNsb3BlIHNob3VsZCBiZSBuZWdhdGl2ZQ0KDQpUaGVzZSB0d28gY2FzZXMgY2FuIGJlIGNvbWJpbmVkIGludG8gb25lIGZvcm11bGE6DQoNCiQkDQpTX2YgPSBcbWF0aHJte3NpZ259KFEpIFxmcmFje25eMlw7IFFeMn17QV4yXDsgUl57NC8zfX0gDQokJA0KDQp3aGVyZSB0aGUgc2lnbiBmdW5jdGlvbiBpcyBkZWZpbmVkIGFzIGluIHRoZSBSLWxhbmd1YWdlLg0KDQo8ZGl2IGNsYXNzPSJxdWVzdGlvbiI+DQpVc2UgdGhlIGhlbHAgb2YgUiB0byBnaXZlIGEgZGVmaW5pdGlvbiBvZiB0aGUgc2lnbiBmdW5jdGlvbiBiZWxvdzoNCjwvZGl2Pg0KDQoNCg0KVXNpbmcgdGhpcyByZXN1bHQsIHRoZSBzaW1wbGlmaWVkIFN0LVZlbmFudCBjYW4gYmUgd3JpdHRlbiBhczoNCg0KJCQNCiBcbWF0aHJte3NpZ259KFEpIFw7IA0KIFxmcmFje25eMlw7IFFeMn17QV4yXDsgUl57NC8zfX0gPSBTX2YgPSBTX28gLSBcZnJhY3tccGFydGlhbCBhfXtccGFydGlhbCB4fSAgPSANCiAtXGZyYWN7XHBhcnRpYWwgel9ifXtccGFydGlhbCB4fSAtIFxmcmFje1xwYXJ0aWFsIGF9e1xwYXJ0aWFsIHh9ID0gDQogLVxmcmFje1xwYXJ0aWFsIGh9e1xwYXJ0aWFsIHh9DQokJA0KDQpSZXdyaXRpbmcgdGhpcyBnaXZlczogJCRRICA9IC1cZnJhY3sxfXtufVw7IFxtYXRocm17c2lnbn1cQmlnKFxmcmFje1xwYXJ0aWFsIGh9e1xwYXJ0aWFsIHh9XEJpZylcOyBcc3FydHsgXEJpZ3wgXGZyYWN7XHBhcnRpYWwgaH17XHBhcnRpYWwgeH0gXEJpZ3x9XDsgQVw7IFJeezIvM30kJA0KDQpBcyB5b3UgY2FuIHNlZSwgdGhlIGNob2ljZSBvZiAkaCQgYXMgc3RhdGUgaXMgY29udmVuaWVudCBoZXJlLg0KDQojIyMgVGhlIGNvZGUNCg0KYGBge3IsIGV2YWw9VFJVRX0NCkhSLlE9IGZ1bmN0aW9uKHgsc3RhdGUsZ3JhZHN0YXRlKQ0Kew0KICBhID0gSFIuYSh4LHN0YXRlKQ0KICBSID0gSFIuUihhKQ0KICBBID0gSFIuQShhKQ0KICByZXR1cm4oLTEvSFIubipzaWduKGdyYWRzdGF0ZSkqc3FydChhYnMoZ3JhZHN0YXRlKSkqQSpSXigyLzMpKSAjKyAoDQp9DQpgYGANCg0KDQojIyBNb2RlbCAxOiB0aGUgSG9vZ2UgUmFhbSBiYXNlIG1vZGVsDQoNCmBgYHtyLCBldmFsPVRSVUV9DQpIUi5iYWNrd2F0ZXJtb2RlbCA9IG5ld0ZMT1cxRChkb21haW49SFIuZG9tYWluLHN5c3RlbWZsdXhmdW5jdGlvbiA9IEhSLlEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gJ0hvb2dlIFJhYW0gYmFja3dhdGVyJykNCg0KSFIud2Vpci5CQyA9IGZ1bmN0aW9uKHN0YXRlKQ0Kew0KICByZXR1cm4oLUhSLndlaXJRKEhSLmEoSFIubGVuZ3RoLHN0YXRlKSkpDQp9DQpIUi5RaW4gPSAxLjINCnNldC5CQy5mbHV4c3RhdGUoSFIuYmFja3dhdGVybW9kZWwsInJpZ2h0IixIUi53ZWlyLkJDKQ0Kc2V0LkJDLmZpeGVkZmx1eChIUi5iYWNrd2F0ZXJtb2RlbCx3aGVyZT0ibGVmdCIsIkhSLlFpbiIpDQpzZXQuZGlzY3JldGlzYXRpb24oSFIuYmFja3dhdGVybW9kZWwsbm9kZXM9c2VxKDAsSFIubGVuZ3RoLGxlbmd0aD01MCksbWV0aG9kPSdGRScpDQoNCmRvLmluaXRpYWxpemUoSFIuYmFja3dhdGVybW9kZWwsSFIuYndyb3VnaCkNCg0KSFIuYWJvdmVib3R0b20gPSBmdW5jdGlvbih4LHN0YXRlKQ0Kew0KICByZXR1cm4oSFIuYSh4LHN0YXRlKT4wLjEpDQp9DQoNCnNldC5pc2FjY2VwdGFibGUoSFIuYmFja3dhdGVybW9kZWwsSFIuYWJvdmVib3R0b20pDQpgYGANCg0KRGlzY3VzczoNCg0KPGRpdiBjbGFzcz0icXVlc3Rpb24iPg0KLSAgIFRoZSBtaW51cyBzaWduIGluIHRoZSBkZWZpbml0aW9uIG9mIHRoZSBsb3dlciBib3VuZGFyeSBjb25kaXRpb246DQo8L2Rpdj4NCg0KPGRpdiBjbGFzcz0icXVlc3Rpb24iPg0KLSAgIHVzZSB0aGUgaGVscCBvZiBGVkZFMUQgdG8gZGlzY3VzcyB0aGUgcm9sZSBvZiB0aGUgZG91YmxlIHF1b3RlcyBhcm91bmQgSFIuUWluIGluIHRoZSBkZWZpbml0aW9uIG9mIHRoZSB1cHN0cmVhbSBib3VuZGFyeSBjb25kaXRpb246DQo8L2Rpdj4NCg0KPGRpdiBjbGFzcz0icXVlc3Rpb24iPg0KLSAgIGRlc2NyaWJlIHRoZSBhY2NlcHRhYmlsaXR5IGNvbmRpdGlvbiBvZiB0aGUgbW9kZWwgZm9yIHRoZSBsZXZlbCBpbiB3b3JkczoNCjwvZGl2PiAgICANCg0KDQpgYGB7ciwgZXZhbD1UUlVFfQ0Kc29sdmUuc3RlcHMoSFIuYmFja3dhdGVybW9kZWwpDQpwbG90KEhSLmJhY2t3YXRlcm1vZGVsLGZsdXhwbG90PVRSVUUpDQpgYGANCg0KQW5kIGEgbmljZSBwbG90IHRvIGNvbXBhcmUgdGhlIHJvdWdoIChsaW5lYXIpIGFwcHJveGltYXRpb24gd2l0aCB0aGUgc21vb3RoIG9uZToNCg0KYGBge3IsIGV2YWw9VFJVRX0NCnBsb3QoSFIuZG9tYWluLEhSLmJ3cm91Z2goSFIuZG9tYWluKSx5bGltPWMoSFIuYm90ZWxldi5kb3duc3RyZWFtLEhSLmJvdGVsZXYudXBzdHJlYW0rYWVxdWkpLA0KICAgICBtYWluPXBhc3RlKCJCYWNrd2F0ZXIgY3VydmUgZm9yIFE9IixIUi5RaW4pLA0KICAgICB4bGFiPSJsIChtKSIseWxhYj0iaCAobSkiLGNvbD0iZ3JlZW4iLGx0eT0yLGx3ZD00LHR5cGU9ImwiKQ0KbGluZXMoSFIuZG9tYWluLEhSLnpiKEhSLmRvbWFpbiksY29sPSJicm93biIsbHdkPTQpDQpIUi5zdGF0ZXMuMSA9IGRhdGFmcmFtZS5zdGF0ZXMoSFIuYmFja3dhdGVybW9kZWwpDQpsaW5lcyhIUi5zdGF0ZXMuMSxjb2w9ImJsdWUiLGx3ZD00KQ0KbGVnZW5kKCJib3R0b21sZWZ0IiwgaW5zZXQ9LjA1LA0KICAgICAgIGMoImJvdHRvbSIsInJhdyBiYWNrd2F0ZXIiLCJ0cnVlIGJhY2t3YXRlciIpLCANCiAgICAgICBjb2w9YygiYnJvd24iLCJncmVlbiIsImJsdWUiKSxsdHk9YygxLDIsMSksDQogICAgICAgbHdkPTMsaG9yaXo9RkFMU0UpDQpgYGANCg0KIyMgTW9kZWwgMjogYWRkaW5nIGRyYWluYWdlDQoNCkFsbCB0aGUgYXBwcm9hY2hlcyBvbmx5IGNvbnNpZGVyZWQgdGhlIEhvb2dlIFJhYW0gYXMgYSB0cmFuc3BvcnQgcml2ZXI6IHdhdGVyIGNvbWluZyBpbiB1cHN0cmVhbSBoYWQgdG8gYmUgdHJhbnNwb3J0ZWQgZG93bnN0cmVhbS4gSW4gcmVhbGl0eSBpbnB1dCAoaW4gZHJhaW5pbmcgc2l0dWF0aW9ucykgb2Ygd2F0ZXIgZG9lcyBhbHNvIG9jY3VyIGFsb25nIHRoZSB0cmFqZWN0b3J5LiBUaGlzIGlzIGNhbGxlZCAqbGF0ZXJhbCBpbmZsb3cqLiBJbiB0ZXJtcyBvZiB0aGUgRlZGRTFEIHBhY2thZ2UgdGhpcyBpcyBjYWxsZWQgKnNwYXRpYWxmbHV4Ki4gSW4gdGhpcyBleGFtcGxlIHdlIHdpbGwgdGhpbmsgb2YgdGhpcyBmbHV4IGNvbWluZyBmcm9tIGRyYWluaW5nIHRoZSBhcmFiZWwgbGFuZCB3aGVyZSB3YXRlciBjb2xsZWN0ZWQgaW4gZGl0Y2hlcyBhbmQgZHJhaW4gdHViZXMgZW5kaW5nIHVwIGluIHRoZSByaXZlci4gV2Ugd2lsbCB0aGluayBvZiB0aGlzIGRyYWluYWdlIGZsdXggdG8gYmUgdW5pZm9ybSBvdmVyIHRoZSBsZW5ndGggb2YgdGhlIHJpdmVyLiBJbiB0aGlzIGNhc2UgdGhpcyBpcyBjYWxsZWQgSFIubGF0ZXJhbGRyYWluYWdlLiBUaGUgbmFtZSBsYXRlcmFsIGlzIGEgdHlwaWNhbCBuYW1lIGZvciB0aGlzIHR5cGUgb2YgZmx1eGVzIGluIG9wZW4gd2F0ZXI6IGxhdGVyYWwgbWVhbnMgImNvbWluZyBmcm9tIHRoZSBzaWRlIiwgc28gZXh0ZXJuYWwuDQoNCkEgcHJvcGVyIHZhbHVlIGZvciB0aGlzIG51bWJlciBzaG91bGQgY29tZSBmcm9tIGEgZ3JvdW5kd2F0ZXIgbW9kZWwuIEZvciBhbiBleGFtcGxlIHZhbHVlIGhlcmUgd2UganVzdCB0YWtlIDUwJSBvZiB0aGUgaW5mbG93IHVwc3RyZWFtIGFzIGEgdG90YWwsIHRoYXQgaGFzIHRvIGJlIGRpc3RyaWJ1dGVkIG92ZXIgdGhlIGxlbmd0aCBvZiB0aGUgcml2ZXIuDQoNCmBgYHtyLCBldmFsPVRSVUV9DQpIUi5sYXRlcmFsZHJhaW5hZ2UgPSAwLjUqSFIuUWluL0hSLmxlbmd0aA0KYWRkLnNwYXRpYWxmbHV4KEhSLmJhY2t3YXRlcm1vZGVsLHJhdGU9IkhSLmxhdGVyYWxkcmFpbmFnZSIsbmFtZT0iZHJhaW5hZ2UiKQ0KDQpzb2x2ZS5zdGVwcyhIUi5iYWNrd2F0ZXJtb2RlbCx2ZXJib3NlbGV2ZWwgPSAxKQ0KSFIuc3RhdGVzLjIgPSBkYXRhZnJhbWUuc3RhdGVzKEhSLmJhY2t3YXRlcm1vZGVsKQ0Kbm9kZXMgPSBIUi5zdGF0ZXMuMiR4DQoNCnBsb3Qobm9kZXMsSFIuemIobm9kZXMpLGNvbD0iYnJvd24iLHR5cGU9ImwiLGx3ZD00LHlsaW09Yyg5LDE2KSwNCiAgICAgeGxhYj0ibCIseWxhYj0iaCIsbWFpbj1wYXN0ZSgiYmFja3dhdGVyICsgbGF0ZXJhbCBmbG93IEhvb2dlIFJhYW0iKSkNCmxpbmVzKEhSLnN0YXRlcy4xLGNvbD0ib3JhbmdlIixsd2Q9MykNCmxpbmVzKEhSLnN0YXRlcy4yLGNvbD0iYmx1ZSIsbHdkPTMpDQpncmlkKGNvbD0iYmxhY2siKQ0KbGVnZW5kKCJib3R0b21sZWZ0IiwgaW5zZXQ9LjA1LA0KICAgICAgIGxlZ2VuZD1jKCJ3aXRob3V0IGRyYWluYWdlIiwid2l0aCBkcmFpbmFnZSIpLCBjb2w9Yygib3JhbmdlIiwiYmx1ZSIpLA0KICAgICAgIGx3ZD0zLGhvcml6PUZBTFNFKQ0KYGBgDQoNClRoaXMgY29uY2x1ZGVzIHRoZSBmaXJzdCBwYXJ0IGFuZCByZXN1bHRzIGluIHRoZSBiYXNpYyBvcGVuIHdhdGVyIG1vZGVsIHNpbXVsYXRpbmcgZmxvdyBpbiB0aGUgSG9vZ2UgUmFhbS4gIEZvciB0aGUgbGF0ZXJhbCBpbmZsb3cgZHVlIHRvIGRyYWluYWdlIGluIHRoZSBzdXJyb3VuZGluZyBhcmVhIG9mIHRoZSBIb29nZSBSYWFtIHdlIGNob3NlIGFuIGFyYml0cmFyeSB2YWx1ZS4gU3RhcnRpbmcgZnJvbSB0aGUgbmV4dCBwYXJ0ICgyKSwgd2Ugd2lsbCBjYWxjdWxhdGVkIHRoaXMgZHJhaW5hZ2UgYW5kIGFsc28gd2lsbCBkZXRlcm1pbmUgdGhlIHN1cmZhY2UgcnVub2ZmIHdoZW4gYXBwbGljYWJsZS4NCg0KDQojIFBBUlQgMiBUaGUgZ3JvdW5kd2F0ZXIgbW9kZWwNCg0KV2Ugd2lsbCBhc3N1bWUgYSAxRCBncm91bmR3YXRlciBtb2RlbCBzaW11bGF0aW5nIG9uZSBzcGVjaWZpYyBwYXJjZWwsIGVuY2xvc2VkIGJldHdlZW4gdHdvIGRpdGNoZXMgb3IgZHJhaW5zLCB3aXRoaW4gdGhlIGRyYWluYWdlIGFyZWEgb2YgdGhpcyBwYXJ0IG9mIHRoZSBIb29nZSBSYWFtLlwNClRoaXMgbW9kZWwgd2lsbCBjYWxjdWxhdGUgYSBoZWFkIGRpc3RyaWJ1dGlvbiBiZXR3ZWVuIGJvdGggZGl0Y2hlcy4gVGhlIG1vZGVsIGRldGVybWluZXMgaG93IG11Y2ggZHJhaW5hZ2UgZnJvbSB0aGUgZ3JvdW5kd2F0ZXIgYW5kIGRpc2NoYXJnZSBmcm9tIHN1cmZhY2UgcnVub2ZmIChkdXJpbmcgc3Rvcm0gZXZlbnRzKSB3aWxsIGJlIGFsbG9jYXRlZCB0byB0aGUgSG9vZ2UgUmFhbS4gVGhpcyBtb2RlbCBtdXN0IGJlIGltYWdpbmVkIHRvIGJlIHBlcnBlbmRpY3VsYXIgdG8gdGhlIEhvb2dlIFJhYW0gb3BlbiB3YXRlciBtb2RlbC4gTXVsdGlwbHlpbmcgdGhpcyBsZW5ndGggb2YgdGhpcyBtb2RlbCAoZGlzdGFuY2UgYmV0d2VlbiB0aGUgdHdvIGRpdGNoZXMpIHdpdGggYSByZXByZXNlbnRhdGl2ZSAnd2lkdGgnIHRvIGNvbWUgdG8gdGhlIHRvdGFsIGRpc2NoYXJnZWQgYXJlYS4NCg0KIyMgQmFzaWMgZ3JvdW5kd2F0ZXIgZXF1YXRpb24NCg0KU2luY2Ugd2Ugd2lsbCBhc3N1bWUgYSBvbmUgZGltZW5zaW9uYWwgbW9kZWwgYW5kIG9ubHkgaG9yaXpvbnRhbCBmbG93IChEdXB1aXQpIHRoZSBiYXNpYyBmbG93IGVxdWF0aW9uIGlzOg0KDQokJA0KUV97Z3d9ID0gLWtEX3tnd30gKiBcZnJhYyB7XHBhcnRpYWwgSH17XHBhcnRpYWwgeH0NCiQkDQoNCiMjIFJ1bm9mZiBDYXVjaHkgdHlwZSB0b3AgYm91bmRhcnkgY29uZGl0aW9uDQoNCg0KJCQNClFfe3J1bm9mZn09IFxmcmFje0ggLSBIX3tzdXJmYWNlfX17Q197c3VyZmFjZX19DQokJA0KDQojIyBEaW1lbnNpb25zIGFuZCBwYXJhbWV0ZXJzIGdyb3VuZHdhdGVyIG1vZGVsDQoNClRoZSBhdmVyYWdlIGRpdGNoIGRpc3RhbmNlIGlzIDEyNSBtIGFuZCB0aGUgdHJhbnNtaXNzaXZpdHkgaW4gdGhpcyBhcmVhIGlzIGVzdGltYXRlZCBvbiAyOCAkbV4yL2QkIGJhc2VkIG9uIHRoZSBoeWRyYXVsaWMgY29uZHVjdGl2aXR5IG9mIHRoZSBzb2lsIGFuZCB0aGUgdGhpY2tuZXNzLg0KDQpUaGUgYXZlcmFnZSBkaXRjaCBsZXZlbCBpcyBnaXZlbiBhcyB0aGUgYXZlcmFnZSByaXZlciBib3R0b20gbGV2ZWwgKyAyMCAkY20kLg0KDQpUaGUgZW50cmFuY2UgcmVzaXN0YW5jZSwgZHVlIHRvIHNlZGltZW50cyBhdCB0aGUgYm90dG9tIG9mIHRoZSBkaXRjaCwgaXMgMyAkZGF5cyQuDQoNCldoZW4gc3VyZmFjZSBydW5vZmYgb2NjdXJzLCB0aGUgc3RhdGUgZXhjZWVkcyB0aGUgc3VyZmFjZSBsZXZlbCBvZiAxNi4wICRtJCBBTVNMLCBhIHZlcnRpY2FsIHJlc2lzdGFuY2Ugb2YgdGhlIHVwcGVyIHNvaWwgb2YgMjAgZGF5cyAoVmVyYW5kZXJpbmdzcmFwcG9ydGFnZSBMSE0gMy4zMCAyMDE3LCBwZyAyMykgY2FuIGJlIGFzc3VtZWQuDQoNClRoZSBzdGF0aW9uYXJ5IHNpdHVhdGlvbiBpcyBiYXNlZCBvbiB0aGUgYXZlcmFnZSB5ZWFybHkgcmVjaGFyZ2U7IGFib3V0IDAuOCAkbW0vZCQuIEZvciB0aGUgdHJhbnNpZW50IG1vZGVsIGEgc2l0dWF0aW9uIG9mIGEgc3Rvcm0gZXZlbnQgd2l0aCBhIG1heGltdW0gcHJlY2lwaXRhdGlvbiByYXRlIG9mIDM2ICRtbS9ob3VyJCBkdXJpbmcgb25lIGhvdXIsIHdpbGwgYmUgdXNlZCBhcyBmb3JjaW5nLiANCg0KICANCiMgQmFzaWMgc2V0dXANCg0KIyMgUmVxdWlyZWQgZnVuY3Rpb25zDQoNCjxkaXYgY2xhc3M9InF1ZXN0aW9uIj4NCkZpbmlzaCB0aGUgZnVuY3Rpb25zIGluIHRoZSBjaHVuY2sgYmVsb3cgKHJlcGxhY2UgWFhYWCwgWVlZWSkNCg0KYGBge3IgZXZhbCA9IFRSVUV9DQpMID0gMTI1ICMgYXZlcmFnZSBkaXRjaCBkaXN0YW5jZSBpbiBtDQoNCkhfZHJhaW5hZ2UgPSAoSFIuYm90ZWxldi51cHN0cmVhbSArIEhSLmJvdGVsZXYuZG93bnN0cmVhbSkvMiArIDAuMg0KR1cua0QgPSA3ICogNCAjIG0yL2QgI2sgPSA3IG0vZCBhbmQgRCA9IDUgbS9kDQoNClAgPSAwLjAwMDggI2VzdGltYXRlZCBhdmVyYWdlIHByZWNpcGl0YXRpb24gbS9kDQoNCkhfc3VyZmFjZSA9IDE2LjAgI2F2ZXJhZ2Ugc3VyZmFjZSBsZXZlbCBhcmVhIG0NCkNfc3VyZmFjZSA9IDIwICNkIHN1cmZhY2UgdmVydGljYWwgcmVzaXN0YW5jZQ0KQ19kaXRjaCA9IDMgI2QgZW50cmFuY2UgcmVzaXN0YW5jZSBvZiB0aGUgZGl0Y2hlcw0KDQpEaXRjaC5iYyA9IGZ1bmN0aW9uKHN0YXRlKQ0Kew0KICBpZihzdGF0ZSA+IEhfZHJhaW5hZ2UpDQogIHsNCiAgcmV0dXJuKChIX2RyYWluYWdlIC0gc3RhdGUpL0NfZGl0Y2gpDQogIH0NCiAgZWxzZQ0KICB7DQogICAgcmV0dXJuKDApDQogIH0NCn0NCg0KUS5ndyA9IGZ1bmN0aW9uKHgsc3RhdGUsZ3JhZHN0YXRlKQ0Kew0KICByZXR1cm4oLUdXLmtEICogZ3JhZHN0YXRlKQ0KfQ0KDQpRLnNyZmMgPSBmdW5jdGlvbih4LHN0YXRlKQ0Kew0KICBpZihzdGF0ZSA+IEhfc3VyZmFjZSkgDQogIHsNCiAgc3VyZmFjZS5ydW5vZmYgPSAoc3RhdGUgLSBIX3N1cmZhY2UpL0Nfc3VyZmFjZQ0KICByZXR1cm4oLXN1cmZhY2UucnVub2ZmKQ0KICB9DQogIGVsc2UNCiAgeyAgDQogICAgcmV0dXJuKDApDQogIH0NCn0NCmBgYA0KPC9kaXY+DQoNCg0KDQojIyBCYXNpYyBtb2RlbCANCg0KPGRpdiBjbGFzcz0icXVlc3Rpb24iPg0KRmluaXNoIHRoZSBjb2RlIGluIHRoZSBjaHVuayBiZWxvdyAocmVwbGFjZSBYWFhYKQ0KWW91IG1heSBjaG9zZSB3aGljaCB0eXBlIG9mIGRpc2NyZXRpc2F0aW9uIHlvdSB3YW50IHRvIHVzZSAoRkUsIEZWIGV0Yy4pDQoNCmBgYHtyIGV2YWwgPSBUUlVFfQ0KR1cuZG9tYWluID0gYygwLEwpDQpHVy5ub2RlcyA9IHNlcShHVy5kb21haW5bMV0sR1cuZG9tYWluWzJdLGxlbmd0aC5vdXQgPTQwKQ0KR1cuc3RhdCA9IG5ld0ZMT1cxRChkb21haW4gPSBHVy5kb21haW4sc3lzdGVtZmx1eGZ1bmN0aW9uID0gUS5ndywgbmFtZSA9ICJncm91bmR3YXRlciBtb2RlbCIpDQphZGQuc3BhdGlhbGZsdXgobW9kZWwgPSBHVy5zdGF0LHJhdGUgPSAiUCIsbmFtZSA9ICJwcmVjaXBpdGF0aW9uIiApDQphZGQuc3BhdGlhbGZsdXgobW9kZWwgPSBHVy5zdGF0LHJhdGUgPSBRLnNyZmMsbmFtZSA9ICJzdXJmYWNlX3J1bm9mZiIpDQoNCnNldC5kaXNjcmV0aXNhdGlvbihtb2RlbCA9IEdXLnN0YXQsR1cubm9kZXMsbWV0aG9kID0gIkZFIikNCnNldC5CQy5mbHV4c3RhdGUobW9kZWwgPSBHVy5zdGF0LHdoZXJlID0gImxlZnQiLGZ1bmMgPSBEaXRjaC5iYykNCnNldC5CQy5mbHV4c3RhdGUobW9kZWwgPSBHVy5zdGF0LCB3aGVyZSA9ICJyaWdodCIsZnVuYyA9IERpdGNoLmJjKQ0KDQpkby5pbml0aWFsaXplKG1vZGVsID0gR1cuc3RhdCxIX3N1cmZhY2UpDQpzdW1tYXJ5KEdXLnN0YXQpDQpzb2x2ZS5zdGVwcyhHVy5zdGF0LCB2ZXJib3NlbGV2ZWwgPSAxKQ0KcGxvdChHVy5zdGF0LGZsdXhwbG90PVQpDQp3YmFsID0gZGF0YWZyYW1lLmJhbGFuY2UoR1cuc3RhdCkNCmtuaXRyOjoga2FibGUod2JhbCkgI2NyZWF0ZXMgYSB0YWJsZSBpbiB0aGUgaHRtbA0KDQpgYGANCjwvZGl2Pg0KDQoNCg0KDQoNCiMgVHJhbnNpZW50IHNpbXVsYXRpb25zDQoNClRoZSB3YXRlciBiYWxhbmNlIGZvciB0aGUgdHJhbnNpZW50IGdyb3VuZHdhdGVyIG1vZGVsIGNhbiBiZSBnaXZlbiBhczoNCg0KJCQNClFfe3N0b3JhZ2V9PSBRX3tpbnRlcm5hbH0gKyBRX3tleHRlcm5hbH1cXA0KIFMgXGZyYWN7XHBhcnRpYWwgSH17XHBhcnRpYWwgdH0gPSBcZnJhYyB7XHBhcnRpYWx9e1xwYXJ0aWFsIHh9XGxlZnQgKCAta0QgXGZyYWN7XHBhcnRpYWwgSH17XHBhcnRpYWwgeH0gXHJpZ2h0ICkgKyBcc3VtIFFfaQ0KJCQNCg0KVGhlIEludGVybmFsIGFuZCBFeHRlcm5hbCBmbHV4ZXMgYXJlIGFscmVhZHkgaW1wbGVtZW50ZWQgYW5kIG5vdyB3ZSBhcmUgZ29pbmcgdG8gaW5jbHVkZSB0aGUgdHJhbnNpZW50IHBhcnQsIHNlZSBhbHNvIHRoZSBzZWNvbmQgYXNzaWdubWVudCB3aXRoIEZWRkUgaW4gd2VlayB0d28uDQoNClRoZSBzdG9yYWdlIGNvZWZmaWNpZW50IGlzIGVzdGltYXRlZCBvbiA1JS4gRm9yIHRoZSBzdG9ybSBldmVudHMsIHRoZSB0aW1lIHN0ZXAgaXMgc2V0IHRvIDEgaG91ciBhbmQgdGhlIHRvdGFsIHNpbXVsYXRpb24gdGltZSBpcyAxMCBkYXlzLCBzdGFydGluZyB3aXRoIGEgc3RhdGlvbmFyeSBzaXR1YXRpb24sIGZvbGxvd2VkIGJ5IGEgc3Rvcm0gZXZlbnQgb2YgYSBncmFkdWFsbHkgaW5jcmVhc2luZyBwcmVjaXBpdGF0aW9uIHJhdGUgdGlsbCAzNiBtbS9ob3VyIGFuZCBncmFkdWFsbHkgZGVjcmVhc2luZyBhZ2FpbiBkdXJpbmcgMjQgaG91cnMuICANCg0KQSB0aW1lIGxvb3AgaXMgcmVxdWlyZWQgZm9yIHRoaXMgc2ltdWxhdGlvbiBzdGVwcGluZyB0aHJvdWdoIHRoZSBob3VybHkgdGltZSBzdGVwcy4NCg0KQWxzbyB0aGUgcHJldmlvdXMgc3RhdGUgKGhlYWQpIGlzIHJlcXVpcmVkIGFuZCBuZWVkIHRvIGJlIHN0b3JlZCB0ZW1wb3JhcmlseSB0byBkZXRlcm1pbmUgdGhlIHN0b3JhZ2UgY2hhbmdlLiAqKlRpcDoqKiB1c2UgdGhlICdzcGVjaWZpYycgZnVuY3Rpb24gaW4gdGhlIEZWRkUxRCBwYWNrYWdlIGZvciBmb3IgdGhpcy4NCg0KIyMgU3RvcmFnZSBmdW5jdGlvbg0KDQpBIG5ldyAiZXh0ZXJuYWwiIGZsdXggZm9yIHRoZSBzdG9yYWdlIG5vdyBuZWVkIHRvIGJlIGRlZmluZWQ7DQoNClNpbmNlIHRoZSB0aW1lIGRlcml2YXRpdmUgaXMgJFxmcmFje1xwYXJ0aWFsIEh9e1xwYXJ0aWFsIHR9IFxhcHByb3ggXGZyYWMge0hee3QrXERlbHRhIHR9LUhedH17XERlbHRhIHR9JCB3YXRlciBpcyBzdG9yZWQsIGFuZCBub3QgYXZhaWxhYmxlIGZvciBpbnRlcm5hbCBmbG93LCB3aGVuIHRoZSBuZXcgaGVhZCBpcyBoaWdoZXIgdGhhbiB0aGUgcHJldmlvdXMgaGVhZC4NCg0KPGRpdiBjbGFzcz0icXVlc3Rpb24iPg0KRmluaXNoIHRoZSBRLnN0byBmdW5jdGlvbiAocmVwbGFjZSBYWFhYLCBZWVlZKQ0KDQpgYGB7ciBldmFsID0gVFJVRX0NCm9sZC5zdGF0ZSA9IHN0YXRlLmZ1bihtb2RlbCA9IEdXLnN0YXQpICNhIEZVTkNUSU9OICghKSB0byBzYXZlIHRoZSBwcmV2aW91cyBzdGF0ZSBvZiB0aGUgbW9kZWwsIHNvIEggYXQgdD0gdCB0byBjYWxjdWxhdGUgdGhlIG5ldyBIIGF0IHQgPSB0ICsgZGVsdC50IC4NCkdXLlMgPSAwLjA1DQpRLnN0byA9IGZ1bmN0aW9uKHgsc3RhdGUpDQp7DQogIFN0b3JhZ2UuZmx1eCA9IC0gR1cuUyAqIChzdGF0ZSAtIG9sZC5zdGF0ZSh4KSkvZGVsdC50DQogIHJldHVybihTdG9yYWdlLmZsdXgpDQp9DQoNCmBgYA0KPC9kaXY+DQoNCg0KDQojIyBUcmFuc2llbnQgbW9kZWwNCg0KVGhlIHRyYW5zaWVudCBtb2RlbCBpcyB0aGUgc2FtZSBhcyB0aGUgc3RhdGlvbmFyeSBtb2RlbCB3aXRoIHRoZSBhZGRpdGlvbiBvZiB0aGUgc3RvcmFnZSBmbHV4Lg0KDQpgYGB7ciBldmFsPVRSVUV9DQpHVy50cmFucyA9IGNvcHkubW9kZWwoR1cuc3RhdCkNCmFkZC5zcGF0aWFsZmx1eChtb2RlbCA9IEdXLnRyYW5zLHJhdGUgPSBRLnN0byxuYW1lID0gInN0b3JhZ2UiKQ0Kc3VtbWFyeShHVy50cmFucykNCmBgYA0KDQojIyBTdG9ybSBldmVudA0KDQpUaGUgc3Rvcm0gZXZlbnQgaXMgc2ltdWxhdGVkIHdpdGhpbiBvbmUgZGF5ICgyNCBob3Vycykgd2l0aCBhIG1heCBvZiAzNjAgbW0vZCBkdXJpbmcgb25lIGhvdXIgaGFsZiB3YXkgdGhpcyBkYXkuDQoNCmBgYHtyfQ0KUC5wYXR0ZXJuLnJhdGUgPSBjKDAuMDAwOCwwLjAxNSoyNCooc2luKHBpKnJlcCgxLzI0KigxOjI0KSkpKSwwLjAwMDgpDQpQLnBhdHRlcm4uZGF0ZSA9IGMoMCxyZXAoMS8yNCooMToyNCkpLCgxKzEvMjQpKQ0KUC5wYXR0ZXJuID0gYXBwcm94ZnVuKFAucGF0dGVybi5kYXRlLFAucGF0dGVybi5yYXRlLHJ1bGUgPSAyKQ0KcGxvdChQLnBhdHRlcm4uZGF0ZSxQLnBhdHRlcm4oUC5wYXR0ZXJuLmRhdGUpLHR5cGU9ImwiLGNvbCA9ICJibHVlIiwNCiAgICAgbHdkID0gMiwgeGxhYiA9ICJkYXlzIix5bGFiID0gImludGluc2l0eSBpbiBtL2QiLG1haW4gPSAiU3Rvcm0gZXZlbnQgaW4gb25lIGRheSIpDQpncmlkKCkNCmBgYA0KDQojIyBUaW1lIGxvb3ANCg0KPGRpdiBjbGFzcz0icXVlc3Rpb24iPg0KRmluaXNoIHRoZSB0aW1lIGxvb3AgcmVwbGFjaW5nIFhYWFgsIFlZWVksIFpaWlogYW5kIEFBQUEuDQoNCmBgYHtyIGV2YWwgPSBUUlVFfQ0KDQojZGF0YSBjb250YWluZXJzIGZvciByZXN1bHRzDQpHVy5RLnRyYW5zID0gYygpDQpHVy5ILnRyYW5zID0gYygpDQoNCiN1c2luZyB0aGUgc3RhdGlvbmFyeSBoZWFkIG9mIHRoZSBzdGF0aW9uYXJ5IG1vZGVsIGZvciB0aGUgaW5pdGlhbCBoZWFkIGZvciB0aGUgZmlyc3QgdGltZSBzdGVwIGZvciB0aGUgdHJhbnNpZW50IG1vZGVsDQpvbGQuc3RhdGUgPSBzdGF0ZS5mdW4oR1cudHJhbnMpICMgZmlyc3QgaW5pdGlhbCBvbGQgc3RhdGUgdG8gYmUgdXNlZCBpbiB0aGUgd2hpbGUgbG9vcC4NCg0KI3RpbWUgYXNwZWN0cw0KZGVsdC50ID0gMS8yNCAjIDEgaG91cg0Kc3RhcnQudGltZSA9IDANCmVuZC50aW1lID0gIDEwLjAgI2RheXMNCg0KY3VycmVudC50aW1lID0gc3RhcnQudGltZQ0KDQojdGltZSBsb29wDQp3aGlsZSAoY3VycmVudC50aW1lIDwgZW5kLnRpbWUpDQp7DQogIGN1cnJlbnQudGltZSA9IGN1cnJlbnQudGltZSArIGRlbHQudA0KICBQID0gUC5wYXR0ZXJuKGN1cnJlbnQudGltZSkgI3ByZWNpcGl0YXRpb24gZHVyaW5nIHRoZSBzdG9ybSANCiAgc29sdmUuc3RlcHMoR1cudHJhbnMpDQogICNzYXZlIGludGVybWVkaWF0ZSBkYXRhDQogIHdiID0gZGF0YWZyYW1lLmJhbGFuY2UoR1cudHJhbnMpDQogIEdXLlEudHJhbnMgPSByYmluZChHVy5RLnRyYW5zLCBjKGN1cnJlbnQudGltZSx3YlsyLDNdLHdiWzMsMl0sd2JbMywzXSx3Yls0LDJdLHdiWzUsM10pKQ0KICBHVy5ILnRyYW5zID0gcmJpbmQoR1cuSC50cmFucywgZGF0YWZyYW1lLnN0YXRlcyhHVy50cmFucylbLDJdKQ0KICAjc2F2ZSBjdXJyZW50IHN0YXRlIGJlaW5nIHRoZSBvbGQgc3RhdGUgZm9yIHRoZSBuZXh0IHRpbWUgc3RlcA0KICBvbGQuc3RhdGUgPSBzdGF0ZS5mdW4oR1cudHJhbnMpDQp9DQogICNzdG9yaW5nIGFuZCBwbG90dGluZyByZXN1bHRzDQogIEdXLlEudHJhbnMgPSBkYXRhLmZyYW1lKEdXLlEudHJhbnMpDQogIGNvbG5hbWVzKEdXLlEudHJhbnMpID0gYygidGltZSIsICJzdXJmYWNlX3J1bm9mZiIsInN0b3JhZ2VfMmZsb3ciLCJzdG9yYWdlXzJzdG9yYWdlIiwicHJlY2lwIiwiYm91bmRhcnkiKQ0KICANCiAgI3Bsb3Qgc29tZSByZXN1bHRzDQogIFEucmFuZ2UgPSByYW5nZShHVy5RLnRyYW5zJHN1cmZhY2VfcnVub2ZmLEdXLlEudHJhbnMkc3RvcmFnZV8yZmxvdyxHVy5RLnRyYW5zJHN0b3JhZ2VfMnN0b3JhZ2UsR1cuUS50cmFucyRwcmVjaXAsR1cuUS50cmFucyRib3VuZGFyeSkNCiAgcGxvdChHVy5RLnRyYW5zJHRpbWUsR1cuUS50cmFucyRzdXJmYWNlX3J1bm9mZix5bGltID0gUS5yYW5nZSwgdHlwZSA9ICJsIiwgY29sID0gInJlZCIsbHdkID0yLA0KICAgICAgIHlsYWIgPSAiZmx1eCByYXRlcyBpbiBtMi9kIiwgeGxhYiA9ICJ0aW1lIGluIGRheXMiLCBtYWluID0gImZsdXggcmF0ZXMgZ3JvdW5kd2F0ZXIgbW9kZWwiKQ0KICBsaW5lcyhHVy5RLnRyYW5zJHRpbWUsR1cuUS50cmFucyRzdG9yYWdlXzJmbG93LGNvbD0ieWVsbG93NCIsbHdkID0gMikNCiAgbGluZXMoR1cuUS50cmFucyR0aW1lLEdXLlEudHJhbnMkc3RvcmFnZV8yc3RvcmFnZSxjb2w9Im9yYW5nZSIsbHdkID0gMikNCiAgbGluZXMoR1cuUS50cmFucyR0aW1lLEdXLlEudHJhbnMkcHJlY2lwLGNvbD0iYmx1ZSIsIGx3ZCA9IDIpDQogIGxpbmVzKEdXLlEudHJhbnMkdGltZSxHVy5RLnRyYW5zJGJvdW5kYXJ5LGNvbD0iZ3JlZW4iKQ0KICBsZWdlbmQoInRvcHJpZ2h0IiwgaW5zZXQ9LjA1LA0KICAgICAgIGxlZ2VuZD1jKCJydW5vZmYiLCJzdG9yYWdlXzJmbG93Iiwic3RvcmFnZV8yc3RvcmFnZSIsInByZWNpcGl0YXRpb24iLCJkcmFpbmFnZSIpLCBjb2w9YygicmVkIiwieWVsbG93NCIsIm9yYW5nZSIsImJsdWUiLCJncmVlbiIpLA0KICAgICAgIGx3ZD0zLGhvcml6PUZBTFNFKQ0KI2ZpcnN0IDIgZGF5cyAyKjI0ID0gNDggcm93DQogIA0KICBwbG90KEdXLlEudHJhbnMkdGltZVsxOjQ4XSxHVy5RLnRyYW5zJHN1cmZhY2VfcnVub2ZmWzE6NDhdLHlsaW0gPSBRLnJhbmdlLCB0eXBlID0gIm8iLCBjb2wgPSAicmVkIixsd2QgPTIsDQogICAgICAgeWxhYiA9ICJmbHV4IHJhdGVzIGluIG0yL2QiLCB4bGFiID0gInRpbWUgaW4gZGF5cyIsIG1haW4gPSAiZmx1eCByYXRlcyBncm91bmR3YXRlciBtb2RlbCIpDQogIGxpbmVzKEdXLlEudHJhbnMkdGltZVsxOjQ4XSxHVy5RLnRyYW5zJHN0b3JhZ2VfMmZsb3dbMTo0OF0sY29sPSJ5ZWxsb3c0Iixsd2QgPSAyKQ0KICBsaW5lcyhHVy5RLnRyYW5zJHRpbWVbMTo0OF0sR1cuUS50cmFucyRzdG9yYWdlXzJzdG9yYWdlWzE6NDhdLGNvbD0ib3JhbmdlIixsd2QgPSAyKQ0KICBsaW5lcyhHVy5RLnRyYW5zJHRpbWVbMTo0OF0sR1cuUS50cmFucyRwcmVjaXBbMTo0OF0sY29sPSJibHVlIiwgbHdkID0gMikNCiAgbGluZXMoR1cuUS50cmFucyR0aW1lWzE6NDhdLEdXLlEudHJhbnMkYm91bmRhcnlbMTo0OF0sY29sPSJncmVlbiIsIGx3ZCA9IDIpDQogIGxlZ2VuZCgidG9wcmlnaHQiLCBpbnNldD0uMDUsDQogICAgICAgbGVnZW5kPWMoInJ1bm9mZiIsImZsb3cgb3ZlciAoc2F0dXJhdGVkKXN0b3JhZ2UiLCJmbG93IHRvICh1bnNhdHVyYXRlZCkgc3RvcmFnZSIsInByZWNpcGl0YXRpb24iLCJkcmFpbmFnZSIpLCBjb2w9YygicmVkIiwieWVsbG93NCIsIm9yYW5nZSIsImJsdWUiLCJncmVlbiIpLA0KICAgICAgIGx3ZD0zLGhvcml6PUZBTFNFKQ0KZ3JpZCgpICANCg0KR1cuUS5kYXRhID0gc3VtbWFyeShHVy5RLnRyYW5zKSAgDQprbml0cjo6a2FibGUoR1cuUS5kYXRhLCBjYXB0aW9uID0gInN1bW1hcnkgZmxvdyBidWRnZXQgZ3JvdW5kd2F0ZXIgbW9kZWwgaW4gbTIvZCIsIGZvcm1hdCA9ICJzaW1wbGUiKQ0KYGBgDQpUaGUgdGVybSA8Y29kZT5zdG9yYWdlXzJmbG93PC9jb2RlPiBzdGFuZHMgZm9yIHN0b3JhZ2Ugd2hpY2ggaXMgcmVsZWFzZWQgdG8gZmxvdyBhbmQgPGNvZGU+c3RvcmFnZV8yc3RvcmFnZTwvY29kZT4gaXMgdGhlIGFtb3VudCBvZiB3YXRlciB3aGljaCB3aWxsIGJlIHN0b3JlZC4NCjwvZGl2Pg0KDQoNCg0KDQojIFBBUlQgMyBDb3VwbGluZyBvcGVuIHdhdGVyIGFuZCBncm91bmR3YXRlcg0KDQpBICJsb29zZSIgY291cGxpbmcgd2lsbCBiZSBhcHBsaWVkIGFuZCBhbmFseXplZC4gV2l0aCB0aGlzIHRoZSBvcGVuIHdhdGVyIG1vZGVsIHdpbGwgcmVjZWl2ZSBkcmFpbmFnZSBhbmQgc3VyZmFjZSBydW5vZmYgZmx1eGVzIGZyb20gdGhlIGdyb3VuZHdhdGVyIG1vZGVsLiAgDQpMYXRlciBvbiwgb25lIG1heSB1c2UgdGhlIGNhbGN1bGF0ZWQgb3BlbiB3YXRlciBsZXZlbCB0byBhZGp1c3QgdGhlIGRyYWluIGRlcHRoIGluIHRoZSBncm91bmR3YXRlciBtb2RlbCBhY2hpZXZpbmcgYSBmdWxsIGNvdXBsaW5nLiBUaGlzIHdpbGwgcmVzdWx0IGluIGEgZnVsbCBjb3VwbGluZzogZXhjaGFuZ2luZyBmbHV4ZXMgZnJvbSBncm91bmR3YXRlciB0byBvcGVuIHdhdGVyIGFuZCBkcmFpbiBkZXB0aCBmcm9tIG9wZW4gd2F0ZXIgdG8gZ3JvdW5kd2F0ZXINCg0KDQojIyMgU2NhbGluZyBvZiBEcmFpbmFnZSBhbmQgc3VyZmFjZSBydW5vZmYNCg0KVGhlIHByZS1jYWxjdWxhdGVkIGRyYWluYWdlIGFuZCBzdXJmYWNlIHJ1bm9mZiBvZiB0aGUgZ3JvdW5kd2F0ZXIgbW9kZWwgYXJlIHVzZWQgYXMgaW5wdXQgKCJmb3JjaW5nIikgZm9yIHRoZSBvcGVuIHdhdGVyIG1vZGVsIGBIUi5iYWNrd2F0ZXJtb2RlbGAgYW5kIG5lZWQgdG8gYmUgInNjYWxlZCIgdG8gcHJvcGVyIGRpbWVuc2lvbnM7ICRtXjMvcyQuICBcDQpUaGUgZ3JvdW5kd2F0ZXIgZHJhaW5hZ2UgYW5kIHN1cmZhY2UgcnVub2ZmIGZsdXhlcyBhcmUgYmFzZWQgb24gb25lIHR5cGljYWwgcGFyY2VsIGhhdmluZyBhbiBhdmVyYWdlIGRpdGNoIGRpc3RhbmNlIG9mIDEyNSBtLiANClRoZXJlIGFyZSBtYW55IG9mIHRoZXNlIHBhcmNlbHMgaW4gdGhlIGNhdGNobWVudCBhcmVhIG9mIHRoZSBIb29nZSBSYWFtLiBUaGVzZSBkaXNjaGFyZ2UgcmF0ZXMgaGF2ZSB1bml0ICRtXjIvZCQgYW5kIG5lZWQgdG8gYmUgdHJhbnNmb3JtZWQgdG8gdGhlIHRvdGFsIGRpc2NoYXJnaW5nIGFyZWEgb2YgdGhlIEhvb2dlIFJhYW0gaW50byB1bml0cyBvZiAkbV4zL3MkLiBGb3IgdGhpcywgeW91IGNhbiBpbWFnaW5lIGFsbCBwYXJjZWxzLCB3aXRoIGFuIGF2ZXJhZ2UgZGl0Y2ggZGlzdGFuY2Ugb2YgMTI1bSwgd2VyZSBzZXQgYmVoaW5kIGVhY2ggb3RoZXIgcmVzdWx0aW5nIGluIGEsIGxldCdzIGNhbGwgaXQgYSAqZGlmZnVzZSBsZW5ndGgqIGNhbGN1bGF0ZWQgYXM6ICRcZnJhYyB7XHRleHR7dG90YWxfZHJhaW5lZF9hcmVhfX17XHRleHR7ZGl0Y2hfZGlzdGFuY2V9fSQuDQpUaGUgZmlndXJlIGJlbG93IGlsbHVzdHJhdGVzIHRoZSBkZXRlcm1pbmF0aW9uIG9mIHRoZSB0b3RhbCBkaWZmdXNlIGxlbmd0aCA9ICR7XHN1bVx0ZXh0e0xvY2FsUGF0aExlbmd0aH19PVxmcmFje1x0ZXh0e0RyYWluZWQgQXJlYX19e1x0ZXh0e0RpdGNoIERpc3RhbmNlfX0kICBcDQohW2lsbHVzdHJhdGlvbiB0byBpbmRpY2F0ZSBob3cgdGhlIGRpZmZ1c2UgbGVuZ3RoIGlzIGRldGVybWluZWRdKEhvb2dlX3JhYW1fZGlmZnVzZV9sZW5ndGgucG5nKQ0KIFwNCkZpbmFsbHkgdGhlIGdyb3VuZHdhdGVyIGRyYWluYWdlIGFuZCBzdXJmYWNlIHJ1bm9mZiBuZWVkIHRvIGJlIHRyYW5zZm9ybWVkIHRvIGEgbGF0ZXJhbCBpbmZsb3cgcmF0ZSBvZiAkbV4zL3MvbV97SFJjb3Vyc2V9JCBmb3IgdGhlIEhvb2dlIFJhYW0uXA0KVGhlIHRvdGFsIGNvbnNpZGVyZWQgZGlzY2hhcmdpbmcgYXJlYSBvZiB0aGUgSG9vZ2UgUmFhbSBpcyA1MDAgaGEuICANCg0KDQogDQoNCg0KPGRpdiBjbGFzcz0icXVlc3Rpb24iPg0KRGV0ZXJtaW5lIHRoZSBwcm9wZXJseSBzY2FsZWQgZHJhaW5hZ2UgYW5kIHJ1bm9mZiBmb3IgdGhlIEhvb2dlIFJhYW0gbW9kZWwgOyBgSFIuYmFja3dhdGVybW9kZWxgLg0KYGBge3IgZXZhbCA9IFRSVUV9DQojIyBmcm9tIGdyb3VuZHdhdGVyIGRpc2NoYXJnZSB0byBsYXRlcmFsIGluZmxvdyBpbnRvIHRoZSBIb29nZSBSYWFtIG9wZW4gd2F0ZXIgbW9kZWwNCkhSLmRpc2NoYXJnZS5hcmVhID0gNTAwICogMTBeNCNpbiBtMg0KZGF5MnNlYyA9IDg2NDAwICNmcm9tIGRheSB0byBzZWNvbmRzDQptMi5kYXkudG8ubTMuZGF5ID0gICBIUi5kaXNjaGFyZ2UuYXJlYS9MICNmcm9tIG0yL2RheSB0byBtMy9kYXk7IGEgImRpZmZ1c2UgbGVuZ3RoIiBiYXNlZCBvbiB0aGUgdG90YWwgZHJhaW5lZCBhcmVhIGRpdmlkZWQgYnkgdGhlIEwoZGl0Y2ggZGlzdGFuY2UpOyB1bml0IGlzIG0gYmVjYXVzZSBpdCBpcyB1c2VkIHRvIGNvbnZlcnQgbTIvZGF5IHRvIG0zL2RheQ0KDQpRLmRyYWluYWdlLm0zLmQgPSBHVy5RLnRyYW5zJGJvdW5kYXJ5ICogbTIuZGF5LnRvLm0zLmRheSAjIFRvdGFsIGRyYWluYWdlIGluIHRoZSBjYXRjaG1lbnQNClEuZHJhaW5hZ2UubTMucyA9IFEuZHJhaW5hZ2UubTMuZC9kYXkyc2VjIyBUb3RhbCBkcmFpbmFnZSBpbiB0aGUgY2F0Y2htZW50DQpRLmRyYWluYWdlLm0yLnMgPSBRLmRyYWluYWdlLm0zLnMvSFIubGVuZ3RoDQogICNEcmFpbmFnZSBwZXIgbSByaXZlcg0KDQpRLnJ1bm9mZi5tMy5kID0gR1cuUS50cmFucyRzdXJmYWNlX3J1bm9mZiAqIG0yLmRheS50by5tMy5kYXkgIyBUb3RhbCBydW5vZmYgaW4gdGhlIGNhdGNobWVudA0KUS5ydW5vZmYubTMucyA9IFEucnVub2ZmLm0zLmQvZGF5MnNlYyAjIFRvdGFsIHJ1bm9mZiBpbiB0aGUgY2F0Y2htZW50DQpRLnJ1bm9mZi5tMi5zID0gUS5ydW5vZmYubTMucy9IUi5sZW5ndGggIyBSdW5vZmYgcGVyIG0gcml2ZXINCg0KYGBgDQo8L2Rpdj4NCg0KDQoNCjxkaXYgY2xhc3M9InF1ZXN0aW9uIj4NCkNsb3NlbHkgaW5zcGVjdCB0aGUgZm9sbG93aW5nIGNodW5rIHJ1bm5pbmcgdGhlIG9wZW4gd2F0ZXIgbW9kZWwgYEhSLmJhY2t3YXRlcm1vZGVsYCB3aXRoIGRyYWluYWdlIGFuZCBzdXJmYWNlIGZsdXhlcyBjb21pbmcgZnJvbSB0aGUgZ3JvdW5kd2F0ZXIgbW9kZWwuICANCjwvZGl2Pg0KDQpgYGB7ciBldmFsPSBUUlVFLCBhbmltYXRpb24xLCAgZmlnLnNob3c9J2FuaW1hdGUnLCBhbmlvcHRzPWMoJ2NvbnRyb2xzJywnbG9vcCcpLCBpbnRlcnZhbD0wLjF9DQoNCiMjIyNwcmVwYXJpbmcgb3BlbiB3YXRlciBtb2RlbCBmb3IgZHJhaW5hZ2UgYW5kIHN1cmZhY2UgcnVub2ZmDQpzdW1tYXJ5KEhSLmJhY2t3YXRlcm1vZGVsKQ0KcGxvdChIUi5iYWNrd2F0ZXJtb2RlbCxmbHV4cGxvdCA9IFQpDQoNCiMjcmVtb3ZpbmcgdGhlIG9sZCBsYXRlcmFsIGZsdXggYW5kIGFkZGluZyB0aGUgbmV3IGRyYWluYWdlIGFuZCBydW5vZmYgbGF0ZXJhbCBmbHV4ZXMNCnJlbS5zcGF0aWFsZmx1eChIUi5iYWNrd2F0ZXJtb2RlbCxuYW1lID0gImRyYWluYWdlIikNCmFkZC5zcGF0aWFsZmx1eChIUi5iYWNrd2F0ZXJtb2RlbCxyYXRlID0gImRyYWluYWdlIixuYW1lID0gImRyYWluYWdlIikNCmFkZC5zcGF0aWFsZmx1eChIUi5iYWNrd2F0ZXJtb2RlbCxyYXRlID0gInJ1bm9mZiIsIG5hbWUgPSAicnVub2ZmIikNCnN1bW1hcnkoSFIuYmFja3dhdGVybW9kZWwpDQoNCg0KI2RhdGEgY29udGFpbmVycyBmb3IgcmVzdWx0cw0KSFIuUS50cmFucyA9IGMoKQ0KSFIuaC50cmFucyA9IGMoKQ0KSFIuUV91cHN0cmVhbSA9IGMoKQ0KSFIuUV9kb3duc3RyZWFtID0gYygpDQojZGF0YSBjb250YWluZXIgZm9yIHJlc3VsdHMNCg0KI3Bsb3QgZmFjaWxpdHkNCnBsb3Qobm9kZXMsSFIuemIobm9kZXMpLGNvbD0iYnJvd24iLHR5cGU9ImwiLGx3ZD00LHlsaW09YygxMSwxOCksDQogICAgIHhsYWI9ImwiLHlsYWI9ImgiLG1haW49cGFzdGUoIlRyYW5zaWVudCB3YXRlciBsZXZlbHMgSG9vZ2UgUmFhbSIpKQ0KbGluZXMoZGF0YWZyYW1lLnN0YXRlcyhIUi5iYWNrd2F0ZXJtb2RlbCksIGNvbCA9ICJibHVlIiwgbHdkID0gMykNCmdyaWQoKQ0KDQojZGVsdC50ID0gMS8yNCBjb21lcyBmcm9tIGdyb3VuZHdhdGVyIG1vZGVsDQpjdXJyZW50LnRpbWUgPSAwLjANCiNlbmQudGltZSA9IDIgY29tZXMgZnJvbSBncm91bmR3YXRlciBtb2RlbA0KdGltZS5zdGVwID0gMA0KDQojIyMjdGltZSBsb29wIGZvciBIUi5iYWNrd2F0ZXJtb2RlbCB3aXRoIGZsdXhlcyBmcm9tIHRoZSBncm91bmR3YXRlciBtb2RlbA0Kd2hpbGUoY3VycmVudC50aW1lIDwgZW5kLnRpbWUpDQoNCnsNCiAgY3VycmVudC50aW1lID0gY3VycmVudC50aW1lICsgZGVsdC50DQogIHRpbWUuc3RlcCA9IHRpbWUuc3RlcCArIDENCiAgZHJhaW5hZ2UgPSBRLmRyYWluYWdlLm0yLnNbdGltZS5zdGVwXQ0KICBydW5vZmYgPSBRLnJ1bm9mZi5tMi5zW3RpbWUuc3RlcF0NCiAgc29sdmUuc3RlcHMoSFIuYmFja3dhdGVybW9kZWwpDQogICMjc2F2aW5nIGludGVybWVkaWF0ZSByZXN1bHRzDQogIHdiID0gZGF0YWZyYW1lLmJhbGFuY2UoSFIuYmFja3dhdGVybW9kZWwpDQogIEhSLlEudHJhbnMgPSByYmluZChIUi5RLnRyYW5zLCBjKGN1cnJlbnQudGltZSx3Yls0LDJdLHdiWzMsMl0sd2JbMiwyXSx3Yls0LDNdKSkNCiAgSFIuaC50cmFucyA9IHJiaW5kKEhSLmgudHJhbnMsIGRhdGFmcmFtZS5zdGF0ZXMoSFIuYmFja3dhdGVybW9kZWwpWywyXSkNCiAgDQogICNjcmVhdGUgcGxvdCB0byBnZW5lcmF0ZSBhbiBhbmltYXRpb24gaW4gdGhlIGtuaXR0ZWQgSFRNTCBkb2N1bWVudA0KICBwbG90KG5vZGVzLEhSLnpiKG5vZGVzKSxjb2w9ImJyb3duIix0eXBlPSJsIixsd2Q9NCx5bGltPWMoMTEsMTgpLA0KICAgICAgIHhsYWI9ImwiLHlsYWI9ImgiLG1haW49cGFzdGUoIlRyYW5zaWVudCB3YXRlciBsZXZlbHMgSG9vZ2UgUmFhbSIpKQ0KICBsaW5lcyhkYXRhZnJhbWUuc3RhdGVzKEhSLmJhY2t3YXRlcm1vZGVsKSwgY29sID0gImJsdWUiLCBsd2QgPSAzKQ0KICBncmlkKCkNCiAgDQp9DQogICMjIGNyZWF0aW5nIGRhdGEgZnJhbWVzIGZvciB0aGUgcmVzdWx0IG9mIHRoZSBvcGVuIHdhdGVyIG1vZGVsIHdpdGggZHJhaW5hZ2UgYW5kIHN1cmZhY2UgcnVub2ZmIA0KIEhSLlEudHJhbnMgPSBkYXRhLmZyYW1lKEhSLlEudHJhbnMpDQogY29sbmFtZXMoSFIuUS50cmFucykgPSBjKCJ0aW1lIiwiYm91bmRhcnlfdXBzdHJlYW0iLCJydW5vZmYiLCJkcmFpbmFnZSIsImJvdW5kYXJ5X2Rvd25zdHJlYW0iKQ0KIEhSLmgudHJhbnMgPSBkYXRhLmZyYW1lKEhSLmgudHJhbnMpDQpgYGANCg0KIyMjIENoZWNraW5nIGZsdXhlcyBhbmQgdG90YWwgdm9sdW1lcw0KDQpGb3IgYm90aCB0aGUgdHJhbnNpZW50IGdyb3VuZHdhdGVyIGFuZCB0aGUgcHNldWRvLXN0YXRpb25hcnkgb3BlbiB3YXRlciBtb2RlbCB0aGUgdG90YWwgdm9sdW1lcyBvZiB3YXRlciAoaW4gJG1eMyQpIGR1cmluZyB0aGUgc2ltdWxhdGlvbiB0aW1lIGFyZSBjb21wYXJlZCB3aXRoIGJvdGggbW9kZWxzLg0KDQojIyMjIGdyb3VuZHdhdGVyDQoNClZvbHVtZSBiYWxhbmNlIGZvciB0aGUgZ3JvdW5kd2F0ZXIgbW9kZWwgDQokJA0KVG90YWxfe2lufSA9IFxzdW1fe3Q9IDB9Xnt0PVR9UHJlY2lwKHQpXERlbHRhIHRcXA0KVG90YWxfe291dH0gPSBcbGVmdCBceyBcc3VtX3t0PTB9Xnt0PVR9IFFzdG8odCkrXHN1bV97dD0wfV57dD1UfSBSdW5vZmYodCkgKyBcc3VtX3t0PTB9Xnt0PVR9IERyYWluYWdlKHQpIFxyaWdodCBcfSBcRGVsdGEgdA0KJCQNClJlY2FsbCB0aGF0IHRoZSB3YXRlciBiYWxhbmNlIG9mIHRoZSBncm91bmR3YXRlciBtb2RlbCBpcyBleHByZXNzZWQgaW4gJG1eMi9kJC4gVG8gY29tZSB0byB2b2x1bWVzICgkbV4zJCkgb25lIG5lZWQgdG8gY29uc2lkZXIgdGhlICJkaWZmdXNlIGxlbmd0aCIgYW5kIHRoZSB0aW1lIHN0ZXAuDQoNCmBgYHtyIGV2YWw9VFJVRX0NCiMgV2F0ZXIgdm9sdW1lcyBmb3IgdGhlIDFEIGdyb3VuZHdhdGVyIG1vZGVsIGR1cmluZyB0aGUgdG90YWwgc2ltdWxhdGlvbiB0aW1lDQoNCkdXLnZvbHVtZS5kcmFpbmFnZSA9IHN1bShHVy5RLnRyYW5zJGJvdW5kYXJ5KSAqIG0yLmRheS50by5tMy5kYXkgKiBkZWx0LnQNCkdXLnZvbHVtZS5ydW5vZmYgPSBzdW0oR1cuUS50cmFucyRzdXJmYWNlX3J1bm9mZikgKiBtMi5kYXkudG8ubTMuZGF5ICogZGVsdC50DQoNCkdXLnZvbHVtZS5zdG9yYWdlX2ludG8gPSBzdW0oR1cuUS50cmFucyRzdG9yYWdlXzJmbG93KSAqIG0yLmRheS50by5tMy5kYXkgKiBkZWx0LnQNCkdXLnZvbHVtZS5zdG9yYWdlX291dG9mID0gc3VtKEdXLlEudHJhbnMkc3RvcmFnZV8yc3RvcmFnZSkgKiBtMi5kYXkudG8ubTMuZGF5ICogZGVsdC50DQoNCkdXLnZvbHVtZS5wcmVjaXBpdGF0aW9uID0gc3VtKEdXLlEudHJhbnMkcHJlY2lwKSAqIG0yLmRheS50by5tMy5kYXkgKiBkZWx0LnQNCg0KR1cubWlzZml0ID0gR1cudm9sdW1lLnByZWNpcGl0YXRpb24gLSBHVy52b2x1bWUuZHJhaW5hZ2UgLSBHVy52b2x1bWUucnVub2ZmIC0gR1cudm9sdW1lLnN0b3JhZ2Vfb3V0b2YgKyBHVy52b2x1bWUuc3RvcmFnZV9pbnRvDQoNCnZvbHVtZS5iYWwuR1cgPSBkYXRhLmZyYW1lKEdyb3VuZHdhdGVyID0gYygicHJlY2lwaXRhdGlvbiIsInN0b3JhZ2VfMmZsb3ciLCJkcmFpbmFnZSIsInJ1bm9mZiIsInN0b3JhZ2VfMnN0b3JhZ2UiLCJ0b3RhbCIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgVm9sdW1lX2luID0gYyhHVy52b2x1bWUucHJlY2lwaXRhdGlvbixHVy52b2x1bWUuc3RvcmFnZV9pbnRvLDAsMCwwLHN1bShHVy52b2x1bWUucHJlY2lwaXRhdGlvbixHVy52b2x1bWUuc3RvcmFnZV9pbnRvKSksDQogICAgICAgICAgICAgICAgICAgICAgICBWb2x1bWVfb3V0ID0gYygwLDAsR1cudm9sdW1lLmRyYWluYWdlLEdXLnZvbHVtZS5ydW5vZmYsR1cudm9sdW1lLnN0b3JhZ2Vfb3V0b2Ysc3VtKEdXLnZvbHVtZS5kcmFpbmFnZSxHVy52b2x1bWUucnVub2ZmLEdXLnZvbHVtZS5zdG9yYWdlX291dG9mKSkpIywNCiAgICAgICAgICAgICAgICAgICAgICAgIA0Ka25pdHI6OmthYmxlKHZvbHVtZS5iYWwuR1csZm9ybWF0ID0gInNpbXBsZSIsIGNhcHRpb24gPSAiVG90YWwgdm9sdW1lcyBpbiBtXjMgZ3JvdW5kd2F0ZXIgbW9kZWwiKQ0KDQpgYGANCg0KIyMjIyBvcGVuIHdhdGVyDQoNCiQkDQpUb3RhbF97aW59ID0gXHN1bV97dD0wfV57dD1UfSBib3VuZGFyeV97dXBzdHJlYW19XERlbHRhIHQgKyBcc3VtX3t0PTB9Xnt0PVR9IHJ1bm9mZiBcRGVsdGEgdCArIFxzdW1fe3Q9MH1ee3Q9VH0gZHJhaW5hZ2UgXERlbHRhIHRcXA0KVG90YWxfe291dH0gPSBcc3VtX3t0PTB9Xnt0PVR9IGJvdW5kYXJ5X3tkb3duc3RyZWFtfQ0KJCQNCg0KYGBge3IgZXZhbD1UUlVFfQ0KIyB3YXRlciB2b2x1bWVzIGZvciB0aGUgMUQgb3BlbiB3YXRlciAoSG9vZ2UgUmFhbSkgbW9kZWwgZHVyaW5nIHRoZSB0b3RhbCBzaW11bGF0aW9uIHRpbWUNCkhSLnZvbHVtZS5ibmQudXBzdHJlYW0gPSBzdW0oSFIuUS50cmFucyRib3VuZGFyeV91cHN0cmVhbSkgKiBkZWx0LnQgKiA4NjQwMCAjdW5pdCBkZWx0LnQgaXMgaW4gZGF5cyENCkhSLnZvbHVtZS5ydW5vZmYgPSBzdW0oSFIuUS50cmFucyRydW5vZmYpICogZGVsdC50ICogODY0MDANCkhSLnZvbHVtZS5kcmFpbmFnZSA9IHN1bShIUi5RLnRyYW5zJGRyYWluYWdlKSAqIGRlbHQudCAqIDg2NDAwDQpIUi52b2x1bWUuYm5kLmRvd25zdHJlYW0gPSBzdW0oSFIuUS50cmFucyRib3VuZGFyeV9kb3duc3RyZWFtKSAqIGRlbHQudCAqIDg2NDAwDQoNCkhSLnZvbHVtZS5pbiA9IEhSLnZvbHVtZS5ibmQudXBzdHJlYW0gKyBIUi52b2x1bWUucnVub2ZmICsgSFIudm9sdW1lLmRyYWluYWdlDQpIUi52b2x1bWUub3V0ID0gSFIudm9sdW1lLmJuZC5kb3duc3RyZWFtDQoNCg0KIyMgdGhpcyBzaG91bGQgYWRkIHVwIHRvIHRoZSB0b3RhbCBhbW91bnQgb2YgcHJlY2lwaXRhdGlvbiBkdXJpbmcgdGhlIHNpbXVsYXRpb24gdGltZS4NCg0Kdm9sdW1lLmJhbC5IUiA9IGRhdGEuZnJhbWUoT3Blbl93YXRlciA9IGMoImluZmxvd191cHN0cmVhbSIsInN1cmZhY2VfcnVub2ZmIiwiZHJhaW5hZ2UiLCJvdXRmbG93X2Rvd25zdHJlYW0iLCJ0b3RhbCIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgVm9sdW1lX2luID0gYyhIUi52b2x1bWUuYm5kLnVwc3RyZWFtLEhSLnZvbHVtZS5ydW5vZmYsSFIudm9sdW1lLmRyYWluYWdlLDAsSFIudm9sdW1lLmluKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFZvbHVtZV9vdXQgPSBjKDAsMCwwLEhSLnZvbHVtZS5ibmQuZG93bnN0cmVhbSxIUi52b2x1bWUub3V0KSkNCmtuaXRyOjprYWJsZSh2b2x1bWUuYmFsLkhSLGZvcm1hdC5hcmcgPSBsaXN0KGRpZ2l0cyA9IDgsbnNtYWxsID0gMyksIGNhcHRpb24gPSAiVG90YWwgdm9sdW1lcyBpbiBtXjMgb3BlbiB3YXRlciBtb2RlbCIpICMsZGlnaXRzID0gMTUpDQoNCmBgYA0KQ2hlY2sgd2hldGhlciB0aGUgb3V0cHV0IG9mIHRoZSBncm91bmR3YXRlciBhcmUgdGhlIHNhbWUgaW5wdXRzIGZvciB0aGUgb3BlbiB3YXRlciBtb2RlbC4NCg0KIyMgVGltZSBzZXJpZXMgYW5hbHlzaXMNCg0KSXQgY2FuIGJlIGludGVyZXN0aW5nIHRvIGZvciBleGFtcGxlIGNvbXBhcmUgdGltZSBzZXJpZXMgZm9yIHRoZSBwcmVjaXBpdGF0aW9uIGFuZCBkaXNjaGFyZ2Ugb2YgdGhlIEhvb2dlIFJhYW0uIFdpdGggdGhpcywgb25lIGNhbiBleGFtaW5lIHRoZSByZXNwb25zZSBvZiBhIHN0b3JtIGV2ZW50IG9uIHRoZSBvcGVuIHdhdGVyIC0gZ3JvdW5kd2F0ZXIgc3lzdGVtLg0KDQpgYGB7ciBldmFsPVRSVUV9DQpwbG90KEdXLlEudHJhbnMkdGltZSwgUC5wYXR0ZXJuKEdXLlEudHJhbnMkdGltZSksIGNvbCA9ICJibHVlIiwgbHdkID0yLCB5bGFiID0gIm0vZCIseGxhYiA9ICJkYXlzIix0eXBlPSAibCIsIG1haW4gPSAiVHJhbnNpZW50IGZsdXhlcyBvcGVuIHdhdGVyIG1vZGVsIGluIG0vZCIsIHN1YiA9ICJUb3RhbCBzaW11bGF0aW9uIHRpbWUiKQ0KSFIuZGlzY2hhcmdlLm0uZCA9IEhSLlEudHJhbnMkYm91bmRhcnlfZG93bnN0cmVhbSAqIDg2NDAwIC8gSFIuZGlzY2hhcmdlLmFyZWENCmxpbmVzKEhSLlEudHJhbnMkdGltZSxIUi5kaXNjaGFyZ2UubS5kLGNvbCA9ICJyZWQiLCBsd2QgPTIpDQpIUi5ydW5vZmYubS5kID0gSFIuUS50cmFucyRydW5vZmYgKiA4NjQwMCAvIEhSLmRpc2NoYXJnZS5hcmVhDQpsaW5lcyhIUi5RLnRyYW5zJHRpbWUsSFIucnVub2ZmLm0uZCwgY29sID0gImJyb3duIixsd2QgPSAgMikNCkhSLmRyYWluYWdlLm0uZCA9IEhSLlEudHJhbnMkZHJhaW5hZ2UgKiA4NjQwMCAvIEhSLmRpc2NoYXJnZS5hcmVhDQpsaW5lcyhIUi5RLnRyYW5zJHRpbWUsSFIuZHJhaW5hZ2UubS5kLCBjb2wgPSAiZ3JlZW4iLGx3ZCA9MikNCmdyaWQoKQ0KbGVnZW5kKCJ0b3ByaWdodCIsIGluc2V0PS4wNSwNCiAgICAgICBsZWdlbmQ9YygicHJlY2lwaXRhdGlvbiIsIkhSIGRpc2NoYXJnZSIsInJ1bm9mZiIsImRyYWluYWdlIiksIGNvbD1jKCJibHVlIiwicmVkIiwiYnJvd24iLCJncmVlbiIpLA0KICAgICAgIGx3ZD0zLGhvcml6PUZBTFNFKQ0KDQp0aW1lLm1heC5wcmVjaXAgPSB3aGljaC5tYXgoUC5wYXR0ZXJuKEdXLlEudHJhbnMkdGltZSkpKmRlbHQudA0KYWJsaW5lKHY9dGltZS5tYXgucHJlY2lwLCBjb2wgPSAiYmx1ZSIpDQoNCnRpbWUubWF4LmRpc2NoYXJnZSA9IHdoaWNoLm1heChIUi5RLnRyYW5zJGJvdW5kYXJ5X2Rvd25zdHJlYW0pKmRlbHQudA0KdGltZS5tYXgucnVub2ZmID0gd2hpY2gubWF4KEhSLlEudHJhbnMkcnVub2ZmKSpkZWx0LnQNCnRpbWUubWF4LmRyYWluYWdlID0gd2hpY2gubWF4KEhSLlEudHJhbnMkZHJhaW5hZ2UpKmRlbHQudA0KDQphYmxpbmUodj10aW1lLm1heC5kaXNjaGFyZ2UsY29sPSdyZWQnLGx3ZD0zKQ0KYWJsaW5lKHY9dGltZS5tYXguZHJhaW5hZ2UsIGNvbCA9ICJncmVlbiIpDQoNCmBgYA0KWm9vbWluZyBpbiBmb3IgdGhlIGZpcnN0IHR3byBkYXlzLg0KDQpgYGB7ciBldmFsPVRSVUV9DQpwbG90LnRpbWUgPSBHVy5RLnRyYW5zJHRpbWVbMTo0OF0NCnBsb3QocGxvdC50aW1lLCBQLnBhdHRlcm4ocGxvdC50aW1lKSwgY29sID0gImJsdWUiLCBsd2QgPTIsIHlsYWIgPSAibS9kIix4bGFiID0gImRheXMiLHR5cGU9ICJvIiwgbWFpbiA9ICJUcmFuc2llbnQgZmx1eGVzIG9wZW4gd2F0ZXIgbW9kZWwgaW4gbS9kIiwgc3ViID0gIkZpcnN0IHR3byBkYXlzIHNpbXVsYXRpb24gdGltZSIpDQpIUi5kaXNjaGFyZ2UubS5kID0gSFIuUS50cmFucyRib3VuZGFyeV9kb3duc3RyZWFtICogODY0MDAgLyBIUi5kaXNjaGFyZ2UuYXJlYQ0KbGluZXMoSFIuUS50cmFucyR0aW1lLEhSLmRpc2NoYXJnZS5tLmQsY29sID0gInJlZCIsIGx3ZCA9MikNCkhSLnJ1bm9mZi5tLmQgPSBIUi5RLnRyYW5zJHJ1bm9mZiAqIDg2NDAwIC8gSFIuZGlzY2hhcmdlLmFyZWENCmxpbmVzKEhSLlEudHJhbnMkdGltZSxIUi5ydW5vZmYubS5kLCBjb2wgPSAiYnJvd24iLGx3ZCA9ICAyKQ0KSFIuZHJhaW5hZ2UubS5kID0gSFIuUS50cmFucyRkcmFpbmFnZSAqIDg2NDAwIC8gSFIuZGlzY2hhcmdlLmFyZWENCmxpbmVzKEhSLlEudHJhbnMkdGltZSxIUi5kcmFpbmFnZS5tLmQsIGNvbCA9ICJncmVlbiIsbHdkID0yKQ0KZ3JpZCgpDQpsZWdlbmQoInRvcHJpZ2h0IiwgaW5zZXQ9LjA1LA0KICAgICAgIGxlZ2VuZD1jKCJwcmVjaXBpdGF0aW9uIiwiSFIgZGlzY2hhcmdlIiwicnVub2ZmIiwiZHJhaW5hZ2UiKSwgY29sPWMoImJsdWUiLCJyZWQiLCJicm93biIsImdyZWVuIiksDQogICAgICAgbHdkPTMsaG9yaXo9RkFMU0UpDQoNCnRpbWUubWF4LnByZWNpcCA9IHdoaWNoLm1heChQLnBhdHRlcm4ocGxvdC50aW1lKSkqZGVsdC50DQphYmxpbmUodj10aW1lLm1heC5wcmVjaXAsIGNvbCA9ICJibHVlIikNCg0KdGltZS5tYXguZGlzY2hhcmdlID0gd2hpY2gubWF4KEhSLlEudHJhbnMkYm91bmRhcnlfZG93bnN0cmVhbSkqZGVsdC50DQp0aW1lLm1heC5ydW5vZmYgPSB3aGljaC5tYXgoSFIuUS50cmFucyRydW5vZmYpKmRlbHQudA0KdGltZS5tYXguZHJhaW5hZ2UgPSB3aGljaC5tYXgoSFIuUS50cmFucyRkcmFpbmFnZSkqZGVsdC50DQoNCmFibGluZSh2PXRpbWUubWF4LmRpc2NoYXJnZSxjb2w9J3JlZCcsbHdkPTEpDQphYmxpbmUodj10aW1lLm1heC5kcmFpbmFnZSwgY29sID0gImdyZWVuIikNCmBgYA0KDQpGcm9tIHRoZSBncmFwaHMgaXQncyBjbGVhcmx5IHZpc2libGUgdGhhdCB0aGUgZGlzY2hhcmdlIGlzIGRlbGF5ZWQgY29tcGFyZWQgdG8gdGhlIHByZWNpcGl0YXRpb24uDQpUaGUgbWF4LiBwcmVjaXBpdGF0aW9uIGlzIGF0IGRheSBgdGltZS5tYXgucHJlY2lwYCAoYmx1ZSB2ZXJ0aWNhbCBsaW5lKSBhbmQgdGhlIG1heC4gZGlzY2hhcmdlIGlzIGF0IGRheSA6IGAgdGltZS5tYXguZGlzY2hhcmdlYCAocmVkIHZlcnRpY2FsIGxpbmUpDQoNClRoZSBtb21lbnQgd2hlbiB0aGUgbWF4aW11bSBkaXNjaGFyZ2UgYXQgdGhlIHdlaXIgb2YgdGhlIEhvb2dlIFJhYW0gaXMgcmVhY2hlZCBjb2luY2lkZXMgd2l0aCB0aGUgbWF4LiBydW5vZmYuDQpXaHk/DQpBTlNXRVI6IFJ1bm9mZiBhbmQgZHJhaW5hZ2UgYm90aCBjb21lIGZyb20gdGhlIHNhbWUgMUQgZ3JvdW5kd2F0ZXIgbW9kZWwuIFNpbmNlIHRoaXMgbW9kZWwgaXMgMSBkaW1lbnNpb25hbCwgZnJvbSBkaXRjaCB0byBkaXRjaCwgaXQgZG9lcyBub3QgY29udGFpbiBhIHNwYXRpYWwgZGlzdHJpYnV0aW9uIHdpdGggcGFyY2VsIGZhciBhd2F5IGFuZCBjbG9zZSBieSB0aGUgSG9vZ2UgUmFhbS4gTW9yZW92ZXIgdGhlIDFEIHRyYW5zaWVudCBncm91bmR3YXRlciBtb2RlbCBjYWxjdWxhdGVzIHRoZSBlZmZlY3Qgb2YgcHJlY2lwaXRhdGlvbiBvbiBncm91bmR3YXRlciBzdG9yYWdlLCBkcmFpbmFnZSBhbmQgc3VyZmFjZSBydW5vZmYgaW4gdGhlIHNhbWUgdGltZSBzdGVwLiBJbiBvdGhlciB3b3JkcywgcHJlY2lwaXRhdGlvbiBpcyBkaXN0cmlidXRlZCBvdmVyIHRoZXNlIHRocmVlIGdyb3VuZHdhdGVyIHNpbmtzIGluIHRoZSBzYW1lIHRpbWUuDQoNCg0KDQojIFBBUlQgNCBTZW5zaXRpdml0eSBhbmFseXNpcw0KDQpIZXJlIHdlIHdpbGwgY29uc2lkZXIgdGhlIGxvY2FsIHNlbnNpdGl2aXR5IGFuYWx5c2lzLg0KDQpUaGUgdGFzayBpcyB0byBkZXRlcm1pbmUgd2hhdCB0aGUgdW5jZXJ0YWludHkgb2YgYSBtb2RlbCAoZGVyaXZlZCkgcmVzdWx0IGlzIGR1ZSB0byB0aGUgdXNlZCB0aGUgcGFyYW1ldGVyIHZhbHVlcyBpbiB0aGUgbW9kZWwuDQoNCkZvciBleGFtcGxlOyBzdXBwb3NlIG9uZSB3YW50J3MgdG8gZGV0ZXJtaW5lIHRoZSB1bmNlcnRhaW50eSBvZiB0aGUgbWF4IGhlYWQgaW4gYSBncm91bmR3YXRlciBtb2RlbCwgZHVlIHRvIHRoZSBtb2RlbCBwYXJhbWV0ZXJzIChsaWtlIGtELCBib3VuZGFyeS1jb25kaXRpb25zLCBwcmVjaXBpdGF0aW9uLCBmb3Igd2hpY2ggeW91IG5vdCBoYXZlIGV4YWN0LCBvciBnb29kIHZhbHVlcy4NCg0KRm9yIHRoaXMgdGhlIGZvbGxvd2luZyBhc3BlY3RzIG5lZWQgdG8gYmUgY29uc2lkZXJlZC9kZXRlcm1pbmVkOg0KDQoxLiAgZGVyaXZlZCByZXN1bHQsIGxpa2UgdGhlIG1heCBkaXNjaGFyZ2UgKGluIHRpbWUpIG9mIHRoZSBIb29nZSBSYWFtDQoNCjIuICBsaXN0IG9mIHBhcmFtZXRlcnMgYW5kIGl0IG9wdGltYWwgKGFzc3VtZWQpIHZhbHVlcw0KDQozLiAgbGlzdCBvZiB0aGUgc2NhbGUgb2YgdmFyaWF0aW9uIG9mIHRoZSBwYXJhbWV0ZXJzDQoNCjQuICBsb2NhbCBzZW5zaXRpdml0aWVzIGZvciB0aGUgcGFyYW1ldGVycw0KDQo1LiAgc2NhbGVkIGNvbnRyaWJ1dGlvbnMgdG8gdW5jZXJ0YWludHkgb2YgZWFjaCBwYXJhbWV0ZXINCg0KIyMgRGVyaXZlZCByZXN1bHQNCg0KYWxsIHJlc3VsdHMgb2YgdGhlIGN1cnJlbnQgY291cGxlZCBtb2RlbHM6DQoNCiMjIyBncm91bmR3YXRlciBtb2RlbDoNCg0KMS4gIGhlYWRzLCAkSCQsIGF0IGRpZmZlcmVudCB0aW1lIHN0ZXBzIGFuZCBsb2NhdGlvbnMNCg0KMi4gIHdhdGVyIGJhbGFuY2UgdGVybXMoZm9yIGRpZmZlcmVudCB0aW1lIHN0ZXBzIGFuZCBsb2NhdGlvbnM6DQoNCiAgICAxLiAgYm91bmRhcnkgaW4sIGJvdW5kYXJ5IG91dCAoZHJhaW5hZ2UpDQoNCiAgICAyLiAgaW50ZXJuYWwgZmx1eA0KDQogICAgMy4gIHN0b3JhZ2UNCg0KICAgIDQuICBwcmVjaXBpdGF0aW9uDQoNCiAgICA1LiAgc3VyZmFjZSBydW5vZmYNCg0KVGhlIG1heCBkaXNjaGFyZ2UgaXMgYEhSLlEudHJhbnMkYm91bmRhcnlfZG93bnN0cmVhbVt0aW1lLm1heC5kaXNjaGFyZ2VdYCBpbiAkbV4zL3MkIGFuZCB0YWtlcyBwbGFjZSBhdCB0aW1lIDogYEhSLlEudHJhbnMkdGltZVt0aW1lLm1heC5kaXNjaGFyZ2VdYCBkYXlzLg0KDQoNCiMjIyBvcGVuIHdhdGVyIG1vZGVsOg0KDQoxLiAgd2F0ZXIgbGV2ZWxzIGF0IGRpZmZlcmVudCB0aW1lIHN0ZXBzIGFuZCBsb2NhdGlvbnMNCg0KMi4gIHdhdGVyIGJhbGFuY2UgdGVybXMgKGZvciBkaWZmZXJlbnQgdGltZSBzdGVwcyBhbmQgbG9jYXRpb25zKQ0KDQogICAgMS4gIGluZmx1eCB1cHN0cmVhbQ0KDQogICAgMi4gIG91dGZsdXggZG93bnN0cmVhbQ0KDQojIyBQYXJhbWV0ZXIgbGlzdA0KDQpBbGwgcGFyYW1ldGVycyBjYW4gYmUgdXNlZCBmb3IgYW5hbHlzaXMgb2YgdGhlIHVuY2VydGFpbnR5IG9mIHRoZSBtb2RlbCByZXN1bHQsIG9mICoqYm90aCoqLCBtb2RlbHMuDQoNCiMjIyBncm91bmR3YXRlciBtb2RlbDoNCg0KMS4gIFRyYW5zbWlzc2l2aXR5IG9mIHRoZSBzdWJzb2lsIGBHVy5rRGANCg0KMi4gIEVudHJhbmNlIHJlc2lzdGFuY2Ugb2YgdGhlIGRpdGNoIGBDX2RpdGNoYA0KDQozLiAgQXZlcmFnZSBkcmFpbiBsZXZlbCBvZiB0aGUgYXJlYSBgSF9kcmFpbmFnZWANCg0KNC4gIFZlcnRpY2FsIHJlc2lzdGFuY2UgdG9wIGxheWVyIHNvaWwgYENfc3VyZmFjZWANCg0KNS4gIEF2ZXJhZ2Ugc3VyZmFjZSBsZXZlbCBkcmFpbmFnZSBzeXN0ZW0gYEhfc3VyZmFjZWANCg0KNi4gIFN0b3JhZ2UgY29lZmZpY2llbnQ7IGBHVy5TYA0KDQojIyMgb3BlbiB3YXRlciBtb2RlbDoNCg0KMS4gIHZhbHVlIHdlaXIgY29uc3RhbnQgKGN1cnJlbnRseSBgMS44M2ApDQoNCjIuICBXZWlyIGNyZXN0IGhlaWdodCBgSFIud2Vpci5jcmVzdGANCg0KMy4gIFdlaXIgd2lkdGggYEhSLndlaXIud2lkdGhgDQoNCjQuICBNYW5uaW5nIGNvZWZmaWNpZW50IGBIUi5uYA0KDQo1LiAgUml2ZXIgd2lkdGggYEhSLmJgDQoNCjYuICBTaWRlIHNsb3BlcyBgSFIubWANCg0KDQojIyBTY2FsZSBvZiB2YXJpYXRpb24gKCRcc2lnbWEkKQ0KDQpUaGUgc2NhbGUgb2YgdmFyaWF0aW9uIGNvdWxkIGJlIGJhc2VkIG9uIGxpdGVyYXR1cmUsIGZpZWxkIGV4cGVyaW1lbnRzIG9yIGZvciBleGFtcGxlIG9uIGEgbW9kZWwgY2FsaWJyYXRpb24uDQoNCiMjIENhbGN1bGF0aW9uIGxvY2FsIHNlbnNpdGl2aXRpZXMNCg0KQWZ0ZXIgd2UgaGF2ZSBkZXRlcm1pbmVkIHRoZSBiYXNlIG1vZGVsIHJlc3VsdCwgd2Ugd2lsbCBkZXRlcm1pbmUgdGhlIGxvY2FsIHNlbnNpdGl2aXR5IG9mIHRoZSBiYXNlIHJlc3VsdCB3LnIudC4gcGFyYW1ldGVyOyAkXGZyYWN7XHBhcnRpYWwgTW9kZWxfe3Jlc3VsdH19e1xwYXJ0aWFsIFBfaX0kLg0KDQojIyBTY2FsZWQgY29udHJpYnV0aW9ucyB0byByZXN1bHQgdW5jZXJ0YWludHkNCg0KDQoNCg0KDQoNCiMjIEFTU0lHTk1FTlQNCg0KQ2Fycnkgb3V0IGEgbG9jYWwgc2Vuc2l0aXZpdHkgYW5hbHlzaXMgZm9yIG9uZSBvZiB0aGUgbW9kZWwgcmVzdWx0cywgdGhlIGdyb3VuZHdhdGVyIGFuZCBvcGVuIHdhdGVyIG1vZGVsLiAgDQoNCjEuICBDaG9vc2UgYXQgbGVhc3QgNiBwYXJhbWV0ZXJzLCBwcmVmZXJhYmx5IGZyb20gYm90aCBtb2RlbHMsIHRvIGNhcnJ5IG91dCB0aGUgc2Vuc2l0aXZpdHkgYW5hbHlzaXMgd2l0aA0KMi4gIEVzdGltYXRlIHRoZSBzY2FsZSBvZiB2YXJpYXRpb24gb2YgdGhlc2UgcGFyYW1ldGVycywgYmFzZWQgb24geW91ciBleHBlcmllbmNlLCB3aGF0IHlvdSBoYXZlIGhlYXJkIGR1cmluZyBjb3Vyc2VzIG9yIHNpbXBseSBnb29nbGUgaXQNCjMuICBDcmVhdGUgY2h1bmtzIHRvIGRldGVybWluZSB0aGUgbG9jYWwgc2Vuc2l0aXZpdGllcw0KNC4gIE1ha2UgdXNlIG9mIHRoZSBwcmUtcHJvZ3JhbW1lZCBgc2Vuc2l0aXZpdHkubG9vcCgpYCBleGVjdXRpbmcgdGltZSBsb29wcyBmb3IgZWFjaCBtb2RlbCBhbmQgc2F2aW5nIGludGVybWVkaWF0ZSBkYXRhDQo1LiAgQ3JlYXRlIGEgY2h1bmsgdG8gY2FsY3VsYXRlIHRoZSBkZXJpdmVkIHVuY2VydGFpbnRpZXMNCg0KVGhpcyA8Y29kZT5zZW5zaXRpdml0eS5sb29wKCk8L2NvZGU+IGNvbnRhaW5zIG1hbnkgbGluZXMgb2YgY29kZSBidXQgYmFzaWNhbGx5IGRvZXMgdGhlIGZvbGxvd2luZzoNCg0KMS4gUnVuIHRoZSB0cmFuc2llbnQgZ3JvdW5kd2F0ZXIgbW9kZWwgDQoyLiBTdG9yZSByZXN1bHRzLCBmbHV4IHJhdGVzIChtMi9kKSBhbmQgaGVhZHMsIG9mIHRoZSBncm91bmR3YXRlciBtb2RlbCANCjMuIE11bHRpcGx5IHRoZSBkcmFpbmFnZSBhbmQgcnVub2ZmIGZsdXggcmF0ZXMgd2l0aCB0aGUgZGlmZnVzZSBsZW5ndGggdG8gY29tZSB0byBtMy9kIGFuZCB0aGVuIHRvIG0zL3MNCjQuIFJ1biB0aGUgdHJhbnNpZW50IG9wZW4gd2F0ZXIgbW9kZWwgd2l0aCB0aGUgY3VycmVudCBkcmFpbmFnZSBhbmQgcnVub2ZmIGZsdXggcmF0ZXMgDQo1LiBTdG9yZSB0aGUgb3BlbiB3YXRlciBmbHV4IHJhdGVzIGFuZCBvcGVuIHdhdGVyIGxldmVscw0KDQoNClRvIHNldCB1cCBhIHByb3BlciBwcm9jZWR1cmUsIGhhdmUgY2xvc2UgbG9vayBhdCB0aGUgTG9jYWwgU2Vuc2l0aXZpdHkgQW5hbHlzaXMgYXNzaWdubWVudCAxLCBMb2NhbCBzZW5zaXRpdml0eSAxRC4NCg0KIyMjIERlcml2ZWQgdW5jZXJ0YWludHkgdG8gYW5hbHlzZTsgdGhlIGBNUkVTVUxUKClgDQoNCkZvciBhIHdhdGVyYm9hcmQgaXQgaXMgaW1wb3J0YW50IHRvIGhhdmUgc29tZSBpbnNpZ2h0IGluIHRoZSB3YXkgc3Rvcm0gZXZlbnRzIGFyZSBwcm9jZXNzZWQgd2l0aGluIHRoZSBhcmVhIHRoZXkgY29udHJvbC4gSXQgaXMgdGhlcmVmb3IgaW1wb3J0YW50IHRoYXQgdGhlIHdhdGVyIG1hbmFnZXJzIGNhbiB0YWtlIG1lYXN1cmVzIGJlZm9yZWhhbmQsIGxpa2UgbG93ZXJpbmcgdGhlIHdlaXIgb2YgdGhlIEhvb2dlIFJhYW0gYmVmb3JlIHRoZSBzdG9ybSBldmVudCBhcnJpdmVzLg0KDQpJbiB0aGlzIGV4YW1wbGUgdGhlIGRlcml2ZWQgdW5jZXJ0YWludHkgd2lsbCBiZSB0aGUgdGhlIG1heC4gd2VpciBkaXNjaGFyZ2UgKGRvd25zdHJlYW0gb2YgdGhlIEhvb2dlIFJhYW0pLg0KDQpUaGVyZSBhcmUgc2V2ZXJhbCBpbnRlcmVzdGluZyBtb2RlbCByZXN1bHRzIGZvciBjbG9zZSBpbnNwZWN0aW9uIHcuci50LiB0aGVpciAnZGVyaXZlZCcgdW5jZXJ0YWludGllcy4gVG8gZ2l2ZSBzb21lIGV4YW1wbGVzOg0KDQoqICBNb21lbnQgd2hlbiB0aGUgd2F0ZXIgbGV2ZWxzIG9mIHRoZSBIb29nZSBSYWFtIHJpc2VzIGZhc3Rlc3QNCiogIFRoZSBidWZmZXIgY2FwYWNpdHkgb2YgdGhlIGFyZWEsIGUuZy4gdGhlIG1vbWVudCB3aGVuIG1vc3Qgb2YgdGhlIHByZWNpcGl0YXRpb24gaXMgc3RvcmVkIGluIHRoZSBncm91bmQgDQoqICBUaGUgbWF4LiB3YXRlciBsZXZlbCBpbiB0aGUgSG9vZ2UgUmFhbQ0KKiAgRWZmZWN0IG9mIHRoZSBwcmVjaXBpdGF0aW9uIGRpc3RyaWJ1dGlvbiBkdXJpbmcgYSBzdG9ybSBldmVudC4NCg0KQWxsIHJlc3VsdHMgYXJlIHN0b3JlZCBpbiB0aGUgZm9sbG93aW5nICJjb250YWluZXJzIiB3aGljaCBhcmUgYWxsIHNpbXBsZSBkYXRhIGZyYW1lLiBTaW1wbHkgY2xpY2sgb24gdGhlbSBpbiB0aGUgdXBwZXIgcmlnaHQgcGFuZWwgKEVudmlyb25tZW50YWwgdGFiKSB0byB3aGF0J3MgYWxsIHN0b3JlZDsNCg0KKiAgYEdXLlEudHJhbnNgIGZvciBhbGwgZmx1eCByYXRlcyBvZiB0aGUgZ3JvdW5kd2F0ZXIgbW9kZWwgaW4gbTIvZDsgZWFjaCByb3cgaXMgb25lIHRpbWUgc3RlcA0KKiAgYEdXLkgudHJhbnNgIGZvciBhbGwgaGVhZHMgaW4gdGhlIGdyb3VuZHdhdGVyIG1vZGVsOyBlYWNoIHJvdyBpcyBvbmUgdGltZSBzdGVwIA0KKiAgYEhSLlEudHJhbnNgIGZvciBhbGwgZmx1eCByYXRlcyBpbiB0aGUgb3BlbiB3YXRlciBtb2RlbCBpbiBtMy9zOyBlYWNoIHJvdyBpcyBvbmUgdGltZSBzdGVwIA0KKiAgYEhSLkgudHJhbnNgIGZvciBhbGwgaGVhZHMgaW4gdGhlIG9wZW4gd2F0ZXIgbW9kZWw7IGVhY2ggcm93IGlzIG9uZSB0aW1lIHN0ZXAgIA0KDQo8ZGl2IGNsYXNzPSJxdWVzdGlvbiI+DQpBcyBhbiBleGFtcGxlLCBgTVJFU1VMVCgpYCBpcyB0aGUgbWF4LiBkaXNjaGFyZ2Ugb2YgdGhlIEhvb2dlIFJhYW0gYXQgdGhlIHdlaXIuICANCg0KYGBge3IgZXZhbCA9IFRSVUV9DQpNUkVTVUxUID0gZnVuY3Rpb24obikNCnsNCiAgaWYobiA9PSAxKSNtYXhpbXVtIGRpc2NoYXJnZSBvZiB0aGUgSG9vZ2UgUmFhbSBhdCB0aGUgd2Vpcg0KICB7DQogICAgbWF4LmRpc2NoYXJnZT0gbWF4KEhSLlEudHJhbnMkYm91bmRhcnlfZG93bnN0cmVhbSkNCiAgICByZXR1cm4obWF4LmRpc2NoYXJnZSkNCiAgfQ0KICBlbHNlIGlmKG4gPT0gMikjTWF4IGhlYWQgb2YgZ3JvdW5kd2F0ZXINCiAgew0KICAgIG1heC5oZWFkPSBtYXgoR1cuSC50cmFucykNCiAgICByZXR1cm4obWF4LmhlYWQpDQogIH0NCiAgZWxzZSBpZihuID09IDMpI01heCB3YXRlciBoZWlnaHQgYXQgSG9vZ2UgUmFhbQ0KICB7DQogICAgbWF4LmhlaWdodD0gbWF4KEhSLmgudHJhbnMkWDUwKQ0KICAgIHJldHVybihtYXguaGVpZ2h0KQ0KICB9DQogIGVsc2UgaWYobiA9PSA0KSAjTWF4IGRyYWluYWdlIG9mIHRoZSBncm91bmR3YXRlcg0KICB7DQogICAgbWF4LmRyYWluLkhSPSBtYXgoR1cuUS50cmFucyRib3VuZGFyeSkNCiAgICByZXR1cm4obWF4LmRyYWluLkhSKQ0KICB9DQogIGVsc2UgaWYobiA9PSA1KSAjTGFnIG9mIG1heGltdW0gcHJlY2lwaXRhdGlvbiBhbmQgdGhlIG1heGltdW0gaGVhZCBvZiBncm91bmR3YXRlcg0KICB7DQogICAgbWF4aW11bV9oZWFkID0gbWF4KEdXLkgudHJhbnMpDQogICAgZXBzID0gMTBeLTcNCiAgICBpbmRleCA9IGMoMCwwKQ0KICAgIGRpbWVuc2lvbiA9IGRpbShHVy5ILnRyYW5zKQ0KICAgIGZvcihpIGluICgxOmRpbShHVy5ILnRyYW5zKVsyXSkpDQogICAgew0KICAgICAgZm9yKGogaW4gKDE6ZGltKEdXLkgudHJhbnMpWzFdKSkNCiAgICAgIHsNCiAgICAgICAgaWYoYWJzKG1heGltdW1faGVhZCAtIEdXLkgudHJhbnNbaixpXSk8ZXBzKSAjY29tcGFyZSBpZiBHVy5ILnRyYW5zIG5vdyA9PSB3aGF0IHdlIHNlYXJjaA0KICAgICAgICB7DQogICAgICAgICAgaW5kZXggPSBjKGksaikgIyBpIGlzIGNvbHVtbiwgaiBpcyByb3csIGlmIGl0IGlzIGVxdWFsIHRha2UgdGhlIGluZGV4DQogICAgICAgIH0NCiAgICAgIH0NCiAgICB9DQogICAgDQogICAgbWF4aW11bV9wcmVjaXBpdGF0aW9uID0gbWF4KEdXLlEudHJhbnMkcHJlY2lwKQ0KICAgIGluZGV4cCA9IGMoMCwwKQ0KICAgIGZvcihpIGluICgxOmxlbmd0aChHVy5RLnRyYW5zJHByZWNpcCkpKQ0KICAgIHsNCiAgICAgICAgaWYoYWJzKG1heGltdW1fcHJlY2lwaXRhdGlvbiAtIEdXLlEudHJhbnMkcHJlY2lwW2ldKTxlcHMpICNjb21wYXJlIGlmIEdXLlEudHJhbnMgbm93ID09IHdoYXQgd2Ugc2VhcmNoDQogICAgICAgIHsNCiAgICAgICAgICBpbmRleHAgPSBjKGkpICMgaWYgaXQgaXMgZXF1YWwgdGFrZSB0aGUgaW5kZXgNCiAgICAgICAgfQ0KICAgIH0NCiAgICANCiAgICByZXR1cm4oKGluZGV4WzJdLWluZGV4cCkqZGVsdC50KQ0KICB9DQp9DQoNCmBgYA0KPC9kaXY+DQoNCkJlIGF3YXJlIHRoYXQgYE1SRVNVTFQoKWAgaXMgYmFzZWQgb24gdGhlIHJlc3VsdHMgd2hpY2ggYXJlIGNvbnRhaW5lZCBpbiB0aGUgKipjdXJyZW50KiogZGF0YSBmcmFtZXMgYEdXLlEudHJhbnMsIFFXLkgudHJhbnMsIEhSLlEudHJhbnMsIEhSLkgudHJhbnNgLiBUaGUgb3JpZ2luYWwgdmFsdWUgb2YgYE1SRVNVTFQoKWAgaXMgYXNzaWduZWQgdG8gYE1fYmFzZWAsIGluIHRoZSBjb2RlIGNodW5rIG9mIHRoZSBmb2xsb3dpbmcgc2VjdGlvbiAiQmFzZSBydW4iLg0KDQoNCg0KIyMjIE1vZGVsIHBhcmFtZXRlcnMgIA0KDQpBcyBhbiBleGFtcGxlIHRoZSBmb2xsb3dpbmcgbW9kZWwgcGFyYW1ldGVycyBjYW4gYmUgdXNlZCBmb3IgdGhlIGFuYWx5c2lzOw0KDQoxLiAgYEhSLm5gIHRoZSBtYW5uaW5nIGNvZWZmaWNpZW50DQoyLiAgYENfZGl0Y2hgIHRoZSBlbnRyYW5jZSByZXNpc3RhbmNlIG9mIHRoZSBkaXRjaGVzIGluIHRoZSBhcmVhDQozLiAgYEhSLlFpbmAgdGhlIGluZmx1eCBpbiAkbV4zL3MkIGF0IHRoZSB1cHN0cmVhbSBlbmQgb2YgdGhlIEhvb2dlIFJhYW0NCjQuICBgSF9zdXJmYWNlYCB0aGUgYXZlcmFnZSBlbGV2YXRpb24gb2YgdGhlIGRyYWluZWQgYXJlYQ0KNS4gIGBIX2RyYWluYWdlYCB0aGUgYXZlcmFnZSBkcmFpbiBsZXZlbCBvZiB0aGUgZHJhaW5lZCBhcmVhDQo2LiAgYEdXLlNgIHN0b3JhZ2UgY29lZmZpY2llbnQgb2YgdGhlIHN1YnNvaWwgKHJlcGxhY2VtZW50IG9mIDUpDQoNCjxkaXYgY2xhc3M9InF1ZXN0aW9uIj4NClNldCB1cCB0aGUgYmFzZSBsaXN0IGZvciB0aGUgYWZvcmVtZW50aW9uZWQgcGFyYW1ldGVycy4gWW91IG1heSBhbHNvIGNob29zZSBkaWZmZXJlbnQgcGFyYW1ldGVycyByaWdodCBhd2F5LiAgDQpgYGB7ciBldmFsID0gVFJVRX0NCmJhc2UgPSBsaXN0KEdXLmtEID0gR1cua0QsIENfZGl0Y2ggPSBDX2RpdGNoLCBIX2RyYWluYWdlID0gSF9kcmFpbmFnZSwgQ19zdXJmYWNlID0gQ19zdXJmYWNlLCBIX3N1cmZhY2UgPSBIX3N1cmZhY2UsIEdXLlMgPSBHVy5TLCBIUi53ZWlyLmNyZXN0ID0gSFIud2Vpci5jcmVzdCwgSFIud2Vpci53aWR0aCA9IEhSLndlaXIud2lkdGgsIEhSLm4gPSBIUi5uLCBIUi5iID0gSFIuYiwgSFIubSA9IEhSLm0pICMgdGhlIGxpc3Qgd2l0aCB0aGUgY3VycmVudCBtb2RlbCBwYXJhbWV0ZXJzDQpzdHIoYmFzZSkjIHByaW50IHRoZSBsaXN0IGZvciBpbnNwZWN0aW9uDQpgYGANCjwvZGl2Pg0KDQoNCg0KDQoNCiMjIyBTY2FsZSBvZiB2YXJpYXRpb24NCg0KPGRpdiBjbGFzcz0icXVlc3Rpb24iPg0KRXN0aW1hdGUgdGhlIHNjYWxlIG9mIHZhcmlhdG9uIGZvciB0aGUgbW9kZWwgcGFyYW1ldGVycy4gIA0KYGBge3IgZXZhbCA9IFRSVUV9DQpzY2FsZSA9IGxpc3QoR1cua0QgPSAwLjUqR1cua0QsIENfZGl0Y2ggPSAwLjYqQ19kaXRjaCwgSF9kcmFpbmFnZSA9IDAuMywgQ19zdXJmYWNlID0gMC42KkNfc3VyZmFjZSwgSF9zdXJmYWNlID0gMC4zLCBHVy5TID0gMC40KkdXLlMsIEhSLndlaXIuY3Jlc3QgPSAwLjEwLCBIUi53ZWlyLndpZHRoID0gMC4wNSwgSFIubiA9IDAuMjUqSFIubiwgSFIuYiA9IDAuMywgSFIubSA9IDAuMipIUi5tKSNsaXN0IG9mIHRoZSBzY2FsZSBvZiB2YXJpYXRpb24gb2YgdGhlIG1vZGVsIHBhcmFtZXRlcnMNCnN0cihzY2FsZSkjcHJpbnQgdGhlIHNjYWxlcyBmb3IgaW5zcGVjdGlvbg0KYGBgDQo8L2Rpdj4NCg0KDQoNCiMjIyBCYXNlIHJ1bg0KDQpUbyBkZXRlcm1pbmUgdGhlIHNlbnNpdGl2aXRpZXMgdGhlIGJhc2UgZGF0YSBuZWVkIHRvIGJlIHNhdmVkOyAgDQpgYGB7ciBldmFsPVRSVUV9DQpmb3IoaSBpbiByYW5nZSgxOjUpKQ0Kew0KICANCn0NCk1fYmFzZSA9IE1SRVNVTFQoMSkNCnByaW50KE1fYmFzZSkNCkdXLnN0YXRlLmJhc2UgPSBHVy5ILnRyYW5zIyBhIGRhdGEuZnJhbWUgZm9yIHRoZSBoZWFkcyBwZXIgdGltZSBzdGVwIG9mIHRoZSBncm91bmR3YXRlciBtb2RlbA0KR1cuZmx1eC5iYXNlID0gR1cuUS50cmFucyMgYSBkYXRhLmZyYW1lIGZvciB0aGUgZmx1eGVzIHBlciB0aW1lIHN0ZXAgb2YgdGhlIGdyb3VuZHdhdGVyIG1vZGVsDQpIUi5zdGF0ZS5iYXNlID0gSFIuaC50cmFucyMgYSBkYXRhLmZyYW1lIGZvciB0aGUgd2F0ZXIgbGV2ZWxzIG9mIHRoZSBIb29nZSBSYWFtIG1vZGVsDQpIUi5mbHV4LmJhc2UgPSBIUi5RLnRyYW5zIyBhIGRhdGFhLmZyYW1lIGZvciB0aGUgZmx1eGVzIHBlciB0aW1lIHN0ZXAgb2YgdGhlIEhvb2dlIFJhYW0gbW9kZWwNCg0KZXBzID0gMC4wMSAjMSUgb2YgdGhlIG9yaWdpbmFsIHZhbHVlIGhvcGVmdWxseSBhdm9pZGluZyByb3VuZGluZyBlcnJvcnMgYW5kIHN0aWxsIGJlIGluIGRlIHByb3BlciBsb2NhbCBkb21haW4uDQpgYGANCg0KIyMjIERldGVybWluZSBsb2NhbCBwYXJhbWV0ZXIgc2VuaXRpdml0aWVzIA0KVGhlIGNob3NlbiBwYXJhbWV0ZXJzIHdpbGwgYmUgYWx0ZXJlZCBieSB0aGUgYGVwc2AgdmFsdWUgYW5kIHJlc2V0IGFmdGVyIHRoZSBydW4uICANCk5leHQgdGhlIGxvY2FsIHNlbnNpdGl2aXR5IG9mIHRoaXMgcGFyYW1ldGVyICRQX2kkIHcuci50LiBgTVJFU1VMVCgpYCB3aWxsIGJlIGRldGVybWluZWQ7ICRcZnJhY3tccGFydGlhbCBcdGV4dHtNUkVTVUxUfX17XHBhcnRpYWwgUF9pfSQuICANCg0KIyMjIyBUaGUgc2Vuc2l0aXR5IGxvb3AgZnVuY3Rpb24NClRvIGF1dG9tYXRlIHRoZSBwcm9jZWR1cmUgdG8gZGV0ZXJtaW5lIHRoZSBsb2NhbCBzZW5zaXRpdml0aWVzIHdlIHdpbGwgbWFrZSB1c2Ugb2YgYSBmdW5jdGlvbiBydW5uaW5nIHRoZSBncm91bmR3YXRlciBtb2RlbCBsb29wIGFuZCB0aGUgb3BlbiB3YXRlciBtb2RlbCBsb29wIGluIGEgc2VxdWVuY2U7IHRoZSBgc2Vuc2l0aXZpdHkubG9vcCgpYCBmdW5jdGlvbjsNCg0KPGRpdiBjbGFzcz0icXVlc3Rpb24iPg0KQ2xvc2VseSBpbnNwZWN0IHdoYXQgaXMgY2FycmllZCBvdXQgZHVyaW5nIG9uZSBwYXNzIG9mIHRoaXMgbG9vcC4gIA0KUHJvYmFibHkgbm90IGEgdmVyeSBmYW1pbGlhciBvcGVyYXRvciBpbiB0aGlzIGxvb3AgaXMgdGhlIGA8PC1gIHN5bWJvbC9vcGVyYXRvci4gSXQgbWVhbnMgdGhhdCBnbG9iYWwgYFBgLCBgZHJhaW5hZ2VgLCBgcnVub2ZmYCwgYEdXLlEudHJhbnMgR1cuSC50cmFucyBIUi5RLnRyYW5zIEhSLmgudHJhbnNgIGRhdGEuZnJhbWVzIGFuZCB0aGUgYG9sZC5zdGF0ZWAgZnVuY3Rpb24gYXJlIGFkanVzdGVkIHdpdGhpbiB0aGlzIGZ1bmN0aW9uIGF2b2lkaW5nIGEgdmVyeSBsYXJnZSBzZXQgb2YgYXJndW1lbnRzIHRvIHBhc3MgdG8gdGhpcyBmdW5jdGlvbi4gIA0KPC9kaXY+DQoNCmBgYHtyfQ0KU2Vuc2l0aXZpdHkubG9vcCA9IGZ1bmN0aW9uKCkNCnsNCiAgIyB0aGUgZ3JvdW5kd2F0ZXIgbW9kZWwgbG9vcCwgR1cgbW9kZWwgZmlyc3QgYmVjYXVzZSB3ZSB1c2UgdGhlIGRyYWluYWdlIGFuZCBydW5vZmYgdG8gdGhlIGJhY2t3YXRlciBtb2RlbCwgYWZ0ZXIgdGhhdCBydW4gYmFja3dhdGVyIG1vZGVsDQogIA0KICAjZGF0YSBjb250YWluZXIgZm9yIHJlc3VsdHMNCiAgR1cuUS50cmFucyA9IGMoKQ0KICBHVy5ILnRyYW5zID0gYygpDQogIHRlbXAucnVub2ZmID0gYygpDQogIA0KDQogICN1c2luZyB0aGUgc3RhdGlvbmFyeSBoZWFkIGZvciB0aGUgaW5pdGlhbCBoZWFkIGZvciB0aGUgZmlyc3QgdGltZSBzdGVwIGZvciB0aGUgdHJhbnNpZW50IG1vZGVsDQogIG9sZC5zdGF0ZSA8PC0gc3RhdGUuZnVuKEdXLnN0YXQpDQogIA0KICBkZWx0LnQgPSAxLzI0ICMgMSBob3VyDQogIHN0YXJ0LnRpbWUgPSAwDQogIGVuZC50aW1lID0gIDEwLjANCiAgDQogIGN1cnJlbnQudGltZSA9IHN0YXJ0LnRpbWUNCiAgDQogIHdoaWxlIChjdXJyZW50LnRpbWUgPCBlbmQudGltZSkNCiAgew0KICAgICN0aW1lIHN0ZXBwaW5nDQogICAgY3VycmVudC50aW1lID0gY3VycmVudC50aW1lICsgZGVsdC50DQogICAgDQogICAgI0dyb3VuZHdhdGVyIG1vZGVsIHJ1bg0KICAgIFAgPDwtIFAucGF0dGVybihjdXJyZW50LnRpbWUpICAjc2hvdWxkIGJlIGEgZ2xvYmFsIHZhcmlhYmxlLCBoZW5jZSA8PC0sIGZvciB0aGUgR1cudHJhbnMgbW9kZWwNCiAgICBzb2x2ZS5zdGVwcyhHVy50cmFucykNCiAgICAjICBwbG90KEdXLnRyYW5zLGZsdXhwbG90ID0gVCkNCiAgICAjc2F2ZSBpbnRlcm1lZGlhdGUgZGF0YQ0KICAgIHdiID0gZGF0YWZyYW1lLmJhbGFuY2UoR1cudHJhbnMpDQogICAgR1cuUS50cmFucyA9IHJiaW5kKEdXLlEudHJhbnMsIGMoY3VycmVudC50aW1lLHdiWzIsM10sd2JbMywyXSx3YlszLDNdLHdiWzQsMl0sd2JbNSwzXSkpDQogICAgR1cuSC50cmFucyA9IHJiaW5kKEdXLkgudHJhbnMsIGRhdGFmcmFtZS5zdGF0ZXMoR1cudHJhbnMpWywyXSkNCiAgICAjc2F2ZSBpbnRlcm1lZGlhdGUgZGF0YSAgDQogICAgb2xkLnN0YXRlIDw8LSBzdGF0ZS5mdW4oR1cudHJhbnMpDQogIH0NCiAgI2Jyb3dzZXIoKQ0KICAjIFN0b3JpbmcgZGF0YSANCiAgR1cuUS50cmFucyA9IGRhdGEuZnJhbWUoR1cuUS50cmFucykNCiAgY29sbmFtZXMoR1cuUS50cmFucykgPSBjKCJ0aW1lIiwgInN1cmZhY2VfcnVub2ZmIiwic3RvcmFnZV9pbiIsInN0b3JhZ2Vfb3V0IiwicHJlY2lwIiwiYm91bmRhcnkiKQ0KICBHVy5ILnRyYW5zID0gZGF0YS5mcmFtZShHVy5ILnRyYW5zKQ0KICANCiAgIyBTY2FsaW5nIHRoZSBncm91bmR3YXRlciBmbHV4ZXMgdG8gbGF0ZXJhbCBpbmZsdXhlcyBmb3IgdGhlIG9wZW4gd2F0ZXIgbW9kZWwNCiAgUS5kcmFpbmFnZS5tMy5kID0gR1cuUS50cmFucyRib3VuZGFyeSAqIG0yLmRheS50by5tMy5kYXkNCiAgUS5kcmFpbmFnZS5tMy5zID0gUS5kcmFpbmFnZS5tMy5kL2RheTJzZWMNCiAgUS5kcmFpbmFnZS5tMi5zID0gUS5kcmFpbmFnZS5tMy5zL0hSLmxlbmd0aA0KICANCiAgUS5ydW5vZmYubTMuZCA9IEdXLlEudHJhbnMkc3VyZmFjZV9ydW5vZmYgKiBtMi5kYXkudG8ubTMuZGF5DQogIFEucnVub2ZmLm0zLnMgPSBRLnJ1bm9mZi5tMy5kL2RheTJzZWMNCiAgUS5ydW5vZmYubTIucyA9IFEucnVub2ZmLm0zLnMvSFIubGVuZ3RoDQogIA0KICANCiAgIyMgdGhlIG9wZW4gd2F0ZXIgbW9kZWwgbG9vcA0KICANCiAgDQogICNkYXRhIGNvbnRhaW5lcnMgZm9yIHJlc3VsdHMNCiAgSFIuUS50cmFucyA9IGMoKQ0KICBIUi5oLnRyYW5zID0gYygpDQogIEhSLlFfdXBzdHJlYW0gPSBjKCkNCiAgSFIuUV9kb3duc3RyZWFtID0gYygpDQogICNkYXRhIGNvbnRhaW5lciBmb3IgcmVzdWx0cw0KICANCiAgI3RpbWUgc3RlcHMgZm9yIHRoZSBsb29wDQogIGN1cnJlbnQudGltZSA9IHN0YXJ0LnRpbWUNCiAgI3RpbWUgc3RlcCBmb3IgcmV0cmlldmluZyBkYXRhIGZyb20gZHJhaW5hZ2UgYW5kIHN1cmZhY2UgdGltZSBzZXJpZXMgb2YgdGhlIGdyb3VuZHdhdGVyIG1vZGVsDQogIHRpbWUuc3RlcCA9c3RhcnQudGltZQ0KICB3aGlsZShjdXJyZW50LnRpbWUgPCBlbmQudGltZSkNCiAgICANCiAgew0KICAgIGN1cnJlbnQudGltZSA9IGN1cnJlbnQudGltZSArIGRlbHQudA0KICAgIHRpbWUuc3RlcCA9IHRpbWUuc3RlcCArIDENCiAgICBkcmFpbmFnZSA8PC0gUS5kcmFpbmFnZS5tMi5zW3RpbWUuc3RlcF0gICNnbG9iYWwgdmFyaWFibGUgZm9yIGdsb2JhbCBIUi5iYWNrd2F0ZXJtb2RlbA0KICAgIHJ1bm9mZiA8PC0gUS5ydW5vZmYubTIuc1t0aW1lLnN0ZXBdICAjZ2xvYmFsIHZhcmlhYmxlIGZvciBnbG9iYWwgSFIuYmFja3dhdGVybW9kZWwNCiAgICANCiAgICBzb2x2ZS5zdGVwcyhIUi5iYWNrd2F0ZXJtb2RlbCkNCiAgICAjI3NhdmluZyBpbnRlcm1lZGlhdGUgcmVzdWx0cw0KICAgIHdiID0gZGF0YWZyYW1lLmJhbGFuY2UoSFIuYmFja3dhdGVybW9kZWwpDQogICAgSFIuUS50cmFucyA9IHJiaW5kKEhSLlEudHJhbnMsIGMoY3VycmVudC50aW1lLHdiWzQsMl0sd2JbMywyXSx3YlsyLDJdLHdiWzQsM10pKQ0KICAgIEhSLmgudHJhbnMgPSByYmluZChIUi5oLnRyYW5zLCBkYXRhZnJhbWUuc3RhdGVzKEhSLmJhY2t3YXRlcm1vZGVsKVssMl0pDQogICAgIyNzYXZpbmcgaW50ZXJtZWRpYXRlIHJlc3VsdHMNCiAgfQ0KICAjYnJvd3NlcigpDQogIEhSLlEudHJhbnMgPSBkYXRhLmZyYW1lKEhSLlEudHJhbnMpDQogIGNvbG5hbWVzKEhSLlEudHJhbnMpID0gYygidGltZSIsImJvdW5kYXJ5X3Vwc3RyZWFtIiwicnVub2ZmIiwiZHJhaW5hZ2UiLCJib3VuZGFyeV9kb3duc3RyZWFtIikNCiAgSFIuaC50cmFucyA9IGRhdGEuZnJhbWUoSFIuaC50cmFucykNCiAgI3JldHVybmluZyB0aGUgbmV3IGRhdGEuZnJhbWVzIHRvIHRoZSBnbG9iYWwgZW52aXJvbm1lbnQgZm9yIGZ1cnRoZXIgYW5hbHlzaXMuDQogIEdXLlEudHJhbnMgPDwtIEdXLlEudHJhbnMNCiAgR1cuSC50cmFucyA8PC0gR1cuSC50cmFucw0KICBIUi5RLnRyYW5zIDw8LSBIUi5RLnRyYW5zDQogIEhSLmgudHJhbnMgPDwtIEhSLmgudHJhbnMNCn0NCmBgYA0KDQoNCiMjIyBMb2NhbCBzZW5zaXRpdml0aWVzDQoNCkVhY2ggbG9jYWwgc2Vuc2l0aXZpdHkgY2FuIG5vdyBiZSBkZXRlcm1pbmVkIHVzaW5nIGEgc2VxdWVuY2Ugb2Y6ICANCg0KKiAgYWRkaW5nIGBlcHNgIHRvIHRoZSBjdXJyZW50IHBhcmFtZXRlciAkUF9pJA0KKiAgcnVubmluZyB0aGUgYFNlbnNpdGl2aXR5Lmxvb3AoKWAgDQoqICBjYWxjdWxhdGluZyAkXGZyYWN7XHBhcnRpYWwgXHRleHR7TVJFU1VMVCgpfX17XHBhcnRpYWwgUF9pfSQNCiogIHN1YnRyYWN0aW5nIGBlcHNgIGZyb20gdGhlIGN1cnJlbnQgcGFyYW1ldGVyLiAgDQoNCjxkaXYgY2xhc3M9InF1ZXN0aW9uIj4NClNldCB1cCB0aGUgc2Vuc2l0aXZpdHkgc2VxdWVuY2UsIHNlZSBhYm92ZSwgdG8gZGV0ZXJtaW5lIHRoZSBsb2NhbCBzZW5zaXRpdml0aWVzIGZvciB0aGUgY2hvc2VuIHBhcmFtZXRlcnMuICANCg0KKipUSVAqKjogeW91IGNhbiB1c2UgYGVwc2AgYmVpbmcgYW4gb2Zmc2V0IHNvLCAkUF9pID0gUF9pICsgZXBzJCBvciB1c2UgaXQgYXMgYSBmcmFjdGlvbjogJFBfaSA9IFBfaSooMSArIGVwcykkDQoNCmBgYHtyIGV2YWwgPSBUUlVFfSANCiMgT3V0cHV0IG9mIGludGVyZXN0Og0KIyAgIDEuIE1heGltdW0gZGlzY2hhcmdlIGF0IHRoZSB3ZWlyIChNUkVTVUxUMSkNCiMgICAyLiBNYXhpbXVtIGhlYWQgb2YgZ3JvdW5kd2F0ZXIgKE1SRVNVTFQyKQ0KIyAgIDMuIE1heGltdW0gd2F0ZXIgaGVpZ2h0IGF0IEhvb2dlIFJhYW0gKE1SRVNVTFQzKQ0KIyAgIDQuIERyYWluYWdlIG9mIHRoZSBncm91bmR3YXRlciAoTUVSU1VMVDQpDQojICAgNS4gTGFnIG9mIG1heGltdW0gcHJlY2lwaXRhdGlvbiBhbmQgdGhlIG1heGltdW0gaGVhZCBvZiBncm91bmR3YXRlciBvciB3aXRoIG1heCBydW5vZmYgKE1SRVNVTFQ1KQ0KDQojIElucHV0DQojIGdyb3VuZHdhdGVyIG1vZGVsOg0KIyAxLiBUcmFuc21pc3Npdml0eSBvZiB0aGUgc3Vic29pbCBHVy5rRA0KIyAyLiBFbnRyYW5jZSByZXNpc3RhbmNlIG9mIHRoZSBkaXRjaCBDX2RpdGNoDQojIDMuIEF2ZXJhZ2UgZHJhaW4gbGV2ZWwgb2YgdGhlIGFyZWEgSF9kcmFpbmFnZQ0KIyA0LiBWZXJ0aWNhbCByZXNpc3RhbmNlIHRvcCBsYXllciBzb2lsIENfc3VyZmFjZQ0KIyA1LiBBdmVyYWdlIHN1cmZhY2UgbGV2ZWwgZHJhaW5hZ2Ugc3lzdGVtIEhfc3VyZmFjZQ0KIyA2LiBTdG9yYWdlIGNvZWZmaWNpZW50OyBHVy5TDQojIA0KIyBvcGVuIHdhdGVyIG1vZGVsOg0KIyAxLiBXZWlyIGNyZXN0IGhlaWdodCBIUi53ZWlyLmNyZXN0DQojIDIuIFdlaXIgd2lkdGggSFIud2Vpci53aWR0aA0KIyAzLiBNYW5uaW5nIGNvZWZmaWNpZW50IEhSLm4NCiMgNC4gUml2ZXIgd2lkdGggSFIuYg0KIyA1LiBTaWRlIHNsb3BlcyBIUi5tDQoNCm5hbWVzID0gbmFtZXMoYmFzZSkNCg0KdmFyUmVzID0gZGF0YS5mcmFtZShNUkVTVUxUMSA9IG51bWVyaWMoKSkNCmZvcihpIGluICgxOmxlbmd0aChiYXNlKSkpDQp7DQogIGJhc2UuY3VycmVudCA9IGFzLm51bWVyaWMoYmFzZVtpXSkgI2Jhc2UgdmFsdWUgb2YgcGFyYW1ldGVyW2ldDQogIHNjYWxlLmN1cnJlbnQgPSBhcy5udW1lcmljKHNjYWxlW2ldKSAjc2NhbGUgb2YgdmFyaWF0aW9uIGluIHRlcm1zIG9mIHN0YW5kYXJkIGRldmlhdGlvbiBvZiBwYXJhbWV0ZXJbaV0NCiAgZXBzID0gc2NhbGUuY3VycmVudCowLjAxICN3ZSB0YWtlIGVwc2lsb24gYXMgMSUgb2YgdGhlIHNjYWxlIG9mIHZhcmlhdGlvbiAoc3RhbmRhcmQgZGV2aWF0aW9uKQ0KICANCiAgbmFtZSA9IG5hbWVzW2ldICN2YXJpYWJsZXMgdGhhdCB3ZSB3YW50IHRvIGNoYW5nZQ0KICANCiAgYXNzaWduKG5hbWUsIGJhc2UuY3VycmVudCtlcHMsIGVudmlyID0gLkdsb2JhbEVudikgI2Fzc2lnbmluZyBuYW1lIHRvIGJhc2UrZXBzDQogIA0KICAjIHNldC5CQy5mbHV4c3RhdGUoSFIuYmFja3dhdGVybW9kZWwsd2hlcmU9InJpZ2h0IixIUi53ZWlyLkJDKQ0KICAjIHNldC5CQy5maXhlZGZsdXgoSFIuYmFja3dhdGVybW9kZWwsd2hlcmU9ImxlZnQiLCJIUi5RaW4iKQ0KICAjIHNldC5CQy5mbHV4c3RhdGUoR1cudHJhbnMsd2hlcmUgPSAibGVmdCIsZnVuYyA9IERpdGNoLmJjKQ0KICAjIHNldC5CQy5mbHV4c3RhdGUoR1cudHJhbnMsd2hlcmUgPSAicmlnaHQiLGZ1bmMgPSBEaXRjaC5iYykNCiAgIyBwcmludChnZXQobmFtZSwgZW52aXIgPSAuR2xvYmFsRW52KSkNCiAgDQogIFNlbnNpdGl2aXR5Lmxvb3AoKSAjcnVubmluZyB0aGUgY291cGxpbmcgbW9kZWwNCiAgDQogIGFzc2lnbihuYW1lLCBiYXNlLmN1cnJlbnQsIGVudmlyID0gLkdsb2JhbEVudikgI2NoYW5naW5nIHZhcmlhYmxlcyBiYWNrIHRvIGJhc2UNCiAgDQogIGFucyA8LSBjKCkgI1RoZSBzaXplIE1SRVNVTFQsIGhvdyBtYW55IHZhcmlhYmxlcyBvZiBpbnRlcmVzdCB3ZSBoYXZlDQogIGZvcihqIGluICgxOjEpKQ0KICB7DQogICAgc2xvcGUgPSAoTVJFU1VMVChqKS1NX2Jhc2UpL2Vwcw0KICAgIHNkID0gc2xvcGUqc2NhbGUuY3VycmVudA0KICAgIHZhciA9IHNkXjINCiAgICANCiAgICBhbnMgPSBjKGFucywgdmFyKQ0KICB9DQogIA0KICB2YXJSZXMgPSByYmluZCh2YXJSZXMsIGFucykNCiAgDQogIHByaW50KGFucykNCn0NCg0KcHJpbnQodmFyUmVzKQ0KYGBgDQo8L2Rpdj4NCg0KDQoNCg0KIyMjIFRvdGFsIGFuZCBwYXJ0aWFsIHZhcmlhbmNlcw0KDQpSZWNhbGwgdGhlIHByZXZpb3VzIExvY2FsIFNlbnNpdGl2aXR5IEFzc2lnbm1lbnQgd2hlcmUgeW91IGNhbGN1bGF0ZWQgdGhlOiAgDQoNCiogIHZhcmlhbmNlcyBmb3IgZWFjaCBwYXJhbWV0ZXIgYmFzZWQgb24gaXRzIHNjYWxlIG9mIHZhcmlhbmNlIGFuZCB0aGUgbG9jYWwgc2Vuc2l0aXZpdHkgb2YgdGhhdCBwYXJhbWV0ZXIgdy5yLnQuIGBNUkVTVUxUKClgDQoqICBjYWxjdWxhdGUgdGhlIHN1bSBvZiB0aGVzZSB2YXJpYW5jZXMNCiogIGNhbGN1bGF0ZSB0aGUgcmVsYXRpdmUgY29udHJpYnV0aW9uIG9mIGVhY2ggcGFyYW1ldGVyIHRvIHRoZSB0b3RhbCB2YXJpYW5jZQ0KKiAgcGxvdCBhIHBpZSBjaGFydCB0byB2aXN1YWxpemUgdGhlIGNvbnRyaWJ1dGlvbnMgdG8gdGhlIGRlcml2ZWQgdW5jZXJ0YWludHkgDQoqICBhIHRhYmxlIG9mIGZyYWN0aW9ucywgYGtuaXRyOjprYWJsZSgpYCBpcyBhbHJlYWR5IGltcGxlbWVudGVkIHRvIGluc3BlY3QgdGhlIHByb2NlbnR1YWwgc2Vuc2l0aXZpdGllcw0KDQo8ZGl2IGNsYXNzPSJxdWVzdGlvbiI+DQpEZXRlcm1pbmUgdGhlIGRlcml2ZWQgdW5jZXJ0YWludHkgYmFzZWQgb24gdGhlIGFmb3JlbWVudGlvbmVkIGxpc3QNCg0KYGBge3IgZXZhbCA9IEZBTFNFfQ0KDQojYSB0YWJsZSBmb3Igc29tZSByZXN1bHRzICJyZWx2YXJNIiBjb250YWlucyB0aGUgcmVsYXRpdmUgdmFyaWFuY2VzIG9mIE1SRVNVTFQNCmtuaXRyOjprYWJsZShhcy5kYXRhLmZyYW1lKHJlbHZhck0pKjEwMCwgY2FwdGlvbiA9ICJwZXJjZW50YWdlIGxvY2FsIHNlbnNpdGl2aXRpZXMiLGFsaWduPSJjIikNCg0KYGBgDQoNCjwvZGl2Pg0KDQoxLjAwOTUyMjUNCg0K